Raycast help (using C#) - raycasting

I have a raycast going upwards from an object which when the player comes in contact with the ray the object changes color. That works but I want to do it so when you touch the ray a second time, the object gets destroyed and I have no idea how to do that. I'm using Unity 2d.
Code: `using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyEnemy : MonoBehaviour //Enemy 3
{
[SerializeField] Transform Enemy3WayPoint;
private Renderer rend;
private Color colorToTurnTo = Color.blue;
void Start()
{
rend = GetComponent<Renderer>();
rend.enabled = true;
Physics2D.queriesStartInColliders = false;
}
private void Update()
{
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector3.up, 5);
if (hitInfo.collider.gameObject.tag == "Player")
{
rend.material.color = colorToTurnTo;
Debug.DrawLine(transform.position, hitInfo.point, Color.white);
}`
There may be a bracket or two I forgot to include, it does work when I test it

I think the simplest solution is to use a variable to keep track of the number of times the ray has been hit by the player.
As for destroying the enemy, you can use the destroy function.
So, something like this:
int hitCount = 0; //add a class variable
void Update(){
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector3.up, 5);
if (hitInfo.collider.gameObject.tag == "Player")
{
hitCount++;
}
if(hitCount == 1)
{
rend.material.color = colorToTurnTo;
Debug.DrawLine(transform.position, hitInfo.point, Color.white);
}
else if(hitCount >= 2)
{
Destroy(gameObject); //this will destroy the gameObject that the component is attached to
}
}
EDIT: It seems the OP's main problem was adding a delay to the events. Here is some updated code that addresses that problem:
bool waitingForFirstHit = true;
bool waitingForSecondHit = false;
float timeDelay = 1.5f;
void Update(){
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector3.up, 5);
if (hitInfo.collider.gameObject.tag == "Player" )
{
if (waitingForFirstHit) {
ChangeColor();
waitingForFirstHit = false;
waitingForSecondHit = true;
}
else if(waitingForSecondHit && timeDelay < 0)
{
DestroyEnemy ();
}
}
if(waitingForSecondHit)
{
timeDelay -= Time.deltaTime;
}
}
void ChangeColor()
{
rend.material.color = colorToTurnTo;
Debug.DrawLine(transform.position, hitInfo.point, Color.white);
}
void DestroyEnemy()
{
Destroy(gameObject);
}
Here is a tutorial on using the Destroy function:
https://unity3d.com/learn/tutorials/topics/scripting/destroy
And here is a link to the docs:
https://docs.unity3d.com/ScriptReference/Object.Destroy.html
Cheers.

Related

GetNode<Timer>("/root/Main/StartTimer") giving System.InvalidCastException

I'm doing the "Your First 2D Game" in Godot because I'm a beginner. I used C# as my script language.
I'm having a problem with GetNode("/root/Main/StartTimer") giving to me a System.InvalidCastException: Specified cast is not valid.
But, StartTimer is the Timer type. So, There's no way it could launch this exception.
I'm exactly at this page of the tutorial:
https://docs.godotengine.org/en/stable/getting_started/first_2d_game/05.the_main_game_scene.html
public void New_Game()
{
Score = 0;
var player = GetNode<Player>("/root/Main/Player");
var startPosition = GetNode<Position2D>("/root/Main/StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("/root/Main/StartTimer").Start();
}
Well, I don't know what to do, since I'm following the tutorial. I think it shouldn't happen because StartTimer is the type Timer, screenshot below:
These are all my scripts:
Main.cs script:
using Godot;
using System;
public class Main : Node
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public int Score;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
GD.Randomize();
New_Game();
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
public void Game_Over()
{
GetNode<Timer>("MobTimer").Stop();
GetNode<Timer>("ScoreTimer").Stop();
}
public void New_Game()
{
Score = 0;
var player = GetNode<Player>("/root/Main/Player");
var startPosition = GetNode<Position2D>("/root/Main/StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("/root/Main/StartTimer").Start();
//Timer StartTimer = GetNode<Timer>("/root/Main/StartTimer");
}
public void OnScoreTimerTimeout()
{
Score++;
}
public void OnStartTimerTimeout()
{
GetNode<Timer>("MobTimer").Start();
GetNode<Timer>("ScoreTimer").Start();
}
public void OnMobTimerTimeout()
{
//Create a new instance of the Mob Scene
var mob = (Mob)MobScene.Instance();
//Choose a random Location on Path2D
var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
mobSpawnLocation.Offset = GD.Randi();
//Set the mon's direction perpendicular to the path direction.
float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
//Set the mob's position to a random location.
mob.Position = mobSpawnLocation.Position;
//Add some randomness to the direction
direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
mob.Rotation = direction;
// Choose the velocity.
var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
mob.LinearVelocity = velocity.Rotated(direction);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
}
Mobs.cs script:
using Godot;
using System;
public class Mob : RigidBody2D
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
animSprite.Playing = true;
string[] mobTypes = animSprite.Frames.GetAnimationNames();
animSprite.Animation = mobTypes[GD.Randi() % mobTypes.Length];
}
// // Called every frame. 'delta' is the elapsed time since the previous
frame.
// public override void _Process(float delta)
// {
//
// }
public void OnVisibilityNotifier2DScreenExited()
{
QueueFree();
}
}
Player.cs script:
using Godot;
using System;
public class Player : Area2D
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
//How fast the player will move (pixel/sec).
[Export]
public int Speed = 400;
[Signal]
public delegate void Hit();
// Size of the game window.
public Vector2 ScreenSize;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
ScreenSize = GetViewportRect().Size;
Hide(); //Player is hidden when the game starts
}
// Called every frame. 'delta' is the elapsed time since the previous
frame.
public override void _Process(float delta)
{
var velocity = Vector2.Zero; //Tha Player's movement vector
if (Input.IsActionPressed("move_right"))
{
velocity.x += 1;
}
if (Input.IsActionPressed("move_left"))
{
velocity.x -= 1;
}
if (Input.IsActionPressed("move_down"))
{
velocity.y += 1;
}
if (Input.IsActionPressed("move_up"))
{
velocity.y -= 1;
}
var animatedSprite = GetNode<AnimatedSprite>("AnimatedSprite");
if (velocity.Length() > 0)
{
velocity = velocity.Normalized() * Speed;
animatedSprite.Play();
}
else
{
animatedSprite.Stop();
}
Position += velocity * delta;
Position = new Vector2(
x: Mathf.Clamp(Position.x, 0, ScreenSize.x),
y: Mathf.Clamp(Position.y, 0, ScreenSize.y)
);
if (velocity.x != 0)
{
animatedSprite.Animation = "walk";
animatedSprite.FlipV = false;
animatedSprite.FlipH = velocity.x < 0; //Here I'm doing a boolean
test
}
else if (velocity.y != 0)
{
animatedSprite.Animation = "up";
animatedSprite.FlipV = velocity.y > 0; //Here I'm doing a boolean
test
}
}
public void On_Player_Body_Entered(PhysicsBody2D body)
{
Hide(); //Player disappears after being hit.
EmitSignal(nameof(Hit));
//Must be deferred as we can't change physics properties on a physics
callback.
GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled",
true);
}
//Reset the player when starting the game
public void Start(Vector2 pos)
{
Position = pos;
Show();
GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
}
}
If you can help me, please.. I'd be very glad and thankful if you could! Thank you!
The reason your game crashes is because of the main.cs script that is attached to all timer nodes.
Since the main.cs script extends the Node object all your timers are becoming Node objects instead of Timer objects. And because Node objects do not have a Start() function your game crashes.
The simple solution to this is to detach the main script from all nodes but the main node.
As Bugfish pointed out, Godot must not attach a script to a node when you connect a signal. Especially not a script from another node. After you deleted your main script from the timers, I recommend to go through the process of signal connection again just to figure out what went wrong the first time.

Resize GameObject over time [duplicate]

I made a test game in unity that makes it so when I click on a button, it spawns a cylinder created from a factory class. I'm trying to make it so when I create the cylinder, its height shrinks over the next 20 seconds. Some methods I found are difficult to translate into what I'm doing. If you could lead me to the right direction, I'd very much appreciate it.
Here's my code for the cylinder class
public class Cylinder : Shape
{
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.transform.localScale = new Vector3(1.0f, Random.Range(1, 2)-1*Time.deltaTime, 1.0f);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
}
This can be done with the Time.deltaTime and Vector3.Lerp in a coroutine function. Similar to Rotate GameObject over time and Move GameObject over time questions. Modified it a little bit to do just this.
bool isScaling = false;
IEnumerator scaleOverTime(Transform objectToScale, Vector3 toScale, float duration)
{
//Make sure there is only one instance of this function running
if (isScaling)
{
yield break; ///exit if this is still running
}
isScaling = true;
float counter = 0;
//Get the current scale of the object to be moved
Vector3 startScaleSize = objectToScale.localScale;
while (counter < duration)
{
counter += Time.deltaTime;
objectToScale.localScale = Vector3.Lerp(startScaleSize, toScale, counter / duration);
yield return null;
}
isScaling = false;
}
USAGE:
Will scale GameObject within 20 seconds:
StartCoroutine(scaleOverTime(cylinder.transform, new Vector3(0, 0, 90), 20f));
Check out Lerp. A general example of how to use it would be something like this:
float t = 0;
Update()
{
t += Time.deltaTime;
cylinder.localScale = new Vector3(1, Mathf.Lerp(2f, 1f, t/3f), 1); // shrink from 2 to 1 over 3 seconds;
}
You will create a new monobehaviour script and add it to your primitive. Then you wil use "Update" method of monobehaviour (or use coroutine) for change object over time.
Monobehaviour must be look like this:
public class ShrinkBehaviour : MonoBehaviour
{
bool isNeedToShrink;
Config currentConfig;
float startTime;
float totalDistance;
public void StartShrink(Config config)
{
startTime = Time.time;
currentConfig = config;
totalDistance = Vector3.Distance(currentConfig.startSize, currentConfig.destinationSize);
isNeedToShrink = true;
transform.localScale = config.startSize;
}
private void Update()
{
if (isNeedToShrink)
{
var nextSize = GetNextSize(currentConfig);
if (Vector3.Distance(nextSize, currentConfig.destinationSize) <= 0.05f)
{
isNeedToShrink = false;
return;
}
transform.localScale = nextSize;
}
}
Vector3 GetNextSize(Config config)
{
float timeCovered = (Time.time - startTime) / config.duration;
var result = Vector3.Lerp(config.startSize, config.destinationSize, timeCovered);
return result;
}
public struct Config
{
public float duration;
public Vector3 startSize;
public Vector3 destinationSize;
}
}
For using this, you must do next:
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
var shrink = cylinder.AddComponent<ShrinkBehaviour>();
shrink.StartShrink(new ShrinkBehaviour.Config() { startSize = Vector3.one * 10, destinationSize = Vector3.one * 1, duration = 20f });
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
You must remember, monobehaviour-script must be in separate file, and must have name similar to monobehaviour-class name. For example, ShrinkBehaviour.cs;

Progressive lag with each collision iteration libGDX

Hello I am developing a game on my spare time with AIDE and libGDX. Though AIDE has some missing methods of the libGDX API and I had to create some workarounds to compensate for the missing methods.
So my problem is that with every instance of a collision the app becomes more and more laggy. No new textures are being drawn and non go away. Just a poor implementation that is meant to push you out of said collision tile. It runs fine until you have a few collisions. My thought is that it creates a new variable with every collision and stores it into memory causing a leak. But I can't really tell if that is the case. Oh I'd like to add that I don't have access to a computer, just my phone. Here is my movement class
move.java
public class move
{
float posX;
float posY;
float touchedOnScreenX;
float touchedOnScreenY;
float centerOfScreenX;
float centerOfScreenY;
float posXSetter;
float posYSetter;
float Speed = 150;
float mapBoundsWidth;
float mapBoundsHeight;
boolean upperBounds;
boolean lowerBounds;
boolean rightBounds;
boolean leftBounds;
boolean tileCollition;
public move() {
}
public void renderMove(float delta) {
if(Gdx.input.isTouched()) {
screenAndTouchInfo();
checkBoundsBoolean();
checkForCollision();
}
}
//slows game down
private void checkForCollision()
{
if (upperBounds == false)
{
if (lowerBounds == false)
{
if (rightBounds == false)
{
if (leftBounds == false)
{
if (tileCollition == false)
{
movement();
}
else
{
collitionSide();
}
}
else
{
posX++;
}
}
else
{
posX--;
}
}
else
{
posY++;
}
}
else
{
posY --;
}
}
private void movement()
{
posYSetter = posY;
posXSetter = posX;
if (touchedOnScreenX < centerOfScreenX)
{
posX -= Gdx.graphics.getDeltaTime() * Speed;
}
else
{
posX += Gdx.graphics.getDeltaTime() * Speed;
}
if (touchedOnScreenY < centerOfScreenY)
{
posY -= Gdx.graphics.getDeltaTime() * Speed;
}
else
{
posY += Gdx.graphics.getDeltaTime() * Speed;
}
if (touchedOnScreenY < centerOfScreenY + 64 && touchedOnScreenY > centerOfScreenY - 64)
{
posY = posYSetter;
}
if (touchedOnScreenX < centerOfScreenX + 64 && touchedOnScreenX > centerOfScreenX - 64)
{
posX = posXSetter;
}
}
//buggy and slows game down. Can push you into tile if input is the opposite of the side
public void collitionSide() {
if (tileCollition == true){
if (touchedOnScreenX < centerOfScreenX)
{
posX = posX +10;
}
else
{
posX = posX - 10;
}
if (touchedOnScreenY < centerOfScreenY)
{
posY = posY +10;
}
else
{
posY = posY -10;
}
}
}
private void screenAndTouchInfo()
{
touchedOnScreenX = Gdx.input.getX();
touchedOnScreenY = (Gdx.graphics.getHeight() - Gdx.input.getY());
centerOfScreenX = Gdx.graphics.getWidth() / 2;
centerOfScreenY = Gdx.graphics.getHeight() / 2;
}
//slows game down
private void checkBoundsBoolean()
{
if (posX > mapBoundsWidth)
{
rightBounds = true;
}
else {
rightBounds = false;
}
if (posY > mapBoundsHeight)
{
upperBounds = true;
}
else {
upperBounds = false;
}
if (posX < mapBoundsWidth - mapBoundsWidth)
{
leftBounds = true;
}
else {
leftBounds = false;
}
if (posY < mapBoundsHeight - mapBoundsHeight)
{
lowerBounds = true;
}
else {
lowerBounds = false;
}
}
public void setTileCollision(boolean tileCollision) {
this.tileCollition = tileCollision;
}
public float getPosX() {
return posX;
}
public float getPosY() {
return posY;
}
public float getTouchedOnScreenX() {
return touchedOnScreenX;
}
public float getTouchedOnScreenY() {
return touchedOnScreenY;
}
public float getCenterOfScreenX() {
return centerOfScreenX;
}
public float getCenterOfScreenY() {
return centerOfScreenY;
}
public void setMapboundsWidth(float width) {
this.mapBoundsWidth = width;
}
public void setMapboundsHeight(float height) {
this.mapBoundsHeight = height;
}
}
I know that is a lot of code to comb through and I am sorry if it isn't always clear what is going on. I refactored it to where it would be a little easier to understand. Oh if anyone could tell me why AIDE is missing some methods of libGDX that would be nice too.The most notable one would be Cell.setTile(). That I know is in libGDX's API and can be found in their documentation. But when I implement it it throws a unknown method of class Cell error. I have researched on how to use the method as well with no avail. Had to create int[][] map and a for loop to draw map with another Texture[]. It works. Lol. Thank you to whomever even took the time to look at my long winded double question. Hopefully someone can tell me why this lag is created from said collisions.
I recommend moving your collision code into a separate thread. This should improve performance significantly:
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
// Physics loop goes here
}
});
Make sure to shutdown the executor when disposing your screen.

How do you make Music shuffle on Android Studio? Using or without using arrays

I need help making the songs Shuffle (mixed) but I don't know how and also I want it to go to another music each time without repeating.
public class MusicMix {
private Music music1, music2, music3, music4, music5,music6,music7,music8,music9,music10,music11,music12,music13,music14,music15,music16,music17;
music1 = Gdx.audio.newMusic(Gdx.files.internal("musicA.mp3"));
music2 = Gdx.audio.newMusic(Gdx.files.internal("musicB.mp3"));
music3 = Gdx.audio.newMusic(Gdx.files.internal("musicC.mp3"));
music4 = Gdx.audio.newMusic(Gdx.files.internal("musicD.mp3"));
music5 = Gdx.audio.newMusic(Gdx.files.internal("musicE.mp3"));
music6 = Gdx.audio.newMusic(Gdx.files.internal("musicF.mp3"));
music7 = Gdx.audio.newMusic(Gdx.files.internal("musicG.mp3"));
music8 = Gdx.audio.newMusic(Gdx.files.internal("musicH.mp3"));
music9 = Gdx.audio.newMusic(Gdx.files.internal("musicJ.mp3"));
music10 = Gdx.audio.newMusic(Gdx.files.internal("musicK.mp3"));
music11 = Gdx.audio.newMusic(Gdx.files.internal("musicL.mp3"));
music12 = Gdx.audio.newMusic(Gdx.files.internal("musicM.mp3"));
music13 = Gdx.audio.newMusic(Gdx.files.internal("musicN.mp3"));
music14 = Gdx.audio.newMusic(Gdx.files.internal("musicO.mp3"));
music15 = Gdx.audio.newMusic(Gdx.files.internal("musicP.mp3"));
music16 = Gdx.audio.newMusic(Gdx.files.internal("musicQ.mp3"));
music17 = Gdx.audio.newMusic(Gdx.files.internal("musicR.mp3"));
}
First of all create first an array that should make your life easier. Instead of manually doing it one by one.
Array<Music> musics = new Array<Music>();
for (int i = 65; i < 25; i++) {
//The ascii of 65 = A
char ascii = (char) i;
Music music = Gdx.audio.newMusic(Gdx.files.internal("music" + ascii + ".mp3"));
musics.add(music);
}
Then after that just call this method. It's a built-in method in Libgdx. This method should answer your question.
musics.shuffle();
I tried to implement your requirement in this way.
public class TestGame extends Game {
private Array<String> musicName;
private Array<Music> musicList;
private IntArray intArray;
#Override
public void create() {
musicName=new Array<String>(new String[]{"sound/x1.ogg","sound/x2.ogg","sound/x3.ogg","sound/x4.ogg","sound/x5.ogg"});
musicList=new Array<Music>();
intArray=new IntArray();
for (String path:musicName) {
Music music=Gdx.audio.newMusic(Gdx.files.internal(path));
musicList.add(music);
intArray.add(musicName.indexOf(path,true));
music.setOnCompletionListener(new OnComplete(this));
}
int value=MathUtils.random(musicList.size-1);
intArray.removeValue(value);
musicList.get(value).play();
}
public class OnComplete implements Music.OnCompletionListener {
private TestGame testGame;
public OnComplete(TestGame game){
testGame=game;
}
#Override
public void onCompletion(Music music) {
testGame.playUnPlayedMusic();
}
}
public void playUnPlayedMusic(){
int value ;
if(intArray.size>0) {
value = intArray.get(MathUtils.random(intArray.size - 1));
intArray.removeValue(value);
}else {
for (int i=0;i<musicList.size;i++)
intArray.add(i);
value = intArray.get(MathUtils.random(intArray.size - 1));
intArray.removeValue(value);
}
Music music= musicList.get(value);
music.play();
}
#Override
public void render() {
}
}

Sound won't play at a specific time in unity

I am trying to make a sound play with the timer goes to 3, 2, 1.
My timer starts at ten and has a three second delay. If I use the following code:
if (tl.myCoolTimer == 10)
{
print("Play Sound");
myAudioSource.Play();
}
It plays the Beep over and over again until the game starts and the counter goes below 10.
If I use the code:
if (tl.myCoolTimer == 3)
{
print("Play Sound");
myAudioSource.Play();
}
It doesn't play the sound at all. It doesn't even print the print statement.
I literally only changed the number. I am not sure why this isn't working.
I have also tried setting it to 3f to see if it is a float issue.
Timer Scripts
This is the starting Timer. it counts down to 3 (then the game starts)
public Text startGameTimerText;
public float startGameTimer = 3;
public void Start ()
{
startGameTimerText = GetComponent<Text> ();
}
public void Update ()
{
startGameTimer -= Time.deltaTime;
startGameTimerText.text = startGameTimer.ToString ("f1");
if (startGameTimer < 0) {
GameObject.Find ("GameStartTimer").SetActive (false);
}
}
This is the Game Timer It starts at 10 and counts down to 0.
public StartGameTimer gt; //this is the script the other timer is on
public Text timerText;
public float myCoolTimer = 10;
public void Start ()
{
timerText = GetComponent<Text> ();
}
public void Update ()
{
if (gt.startGameTimer > 0) {
myCoolTimer = 10;
} else {
myCoolTimer -= Time.deltaTime;
timerText.text = myCoolTimer.ToString ("f1");
}
}
Thanks Joe for the help. Here was my final answer. I know it is hacked, but I haven't figured out the Invoke thing yet. When I set the into it kept playing the entire time it was at "3", so i need to make it play only once.
private AudioSource myAudioSource;
public bool isSoundPlayed;
void Start()
{
myAudioSource = GetComponent<AudioSource>();
isSoundPlayed = false;
}
void Update()
{
if((int)tl.myCoolTimer == 3)
{
if (isSoundPlayed == false)
{
myAudioSource.Play();
isSoundPlayed = true;
}
return;
}
if ((int)tl.myCoolTimer == 2)
{
if (isSoundPlayed == true)
{
myAudioSource.Play();
isSoundPlayed = false;
}
return;
}
if ((int)tl.myCoolTimer == 1)
{
if (isSoundPlayed == false)
{
myAudioSource.Play();
isSoundPlayed = true;
}
return;
}
}

Resources