Trigger-zone reads the same object every frame, it is not supposed to do that and it is messing up the game. It does not happen all the time and I don't know why
Sorry for the messy code, I am not too organized.
There are not many comments (if any) I always forget to put them in.
I am just typing now to try to get it to let me post
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spacereader : MonoBehaviour
{
public int spacesmoved = 0, jiff;
void Start()
{
}
void Update()
{
sharedscript.nummoved = spacesmoved;
if(sharedscript.reset)
{
spacesmoved = 0;
jiff = 0;
}
}
private void OnTriggerEnter(Collider other)
{
Debug.Log(other.tag);
Debug.Log(other.name);
StartCoroutine(spotchecker(other));
if (jiff != 2)
jiff++;
else if (sharedscript.ismoving)
{
spacesmoved++;
}
}
private IEnumerator spotchecker(Collider other)
{
switch (other.tag)
{
case "GO":
break;
case "MEDAVE":
break;
case "CHEST":
break;
case "BALAVE":
break;
case "INCOME":
break;
case "READING":
break;
case "ORIENTAL AVE":
break;
case "CHANCE":
break;
case "VERMONT":
break;
case "CONNAVE":
break;
case "INJAIL":
break;
case "CHARPLACE":
break;
case "COMPANY":
break;
case "STATESAVE":
break;
case "VIRGAVE":
break;
case "PENNRAIL":
break;
case "JAMESPLACE":
break;
case "TENNAVE":
break;
case "NEWYORKAVE":
break;
case "FREEPARK":
break;
case "KENAVE":
break;
case "INDIAVE":
break;
case "ILLAVE":
break;
case "BORAIL":
break;
case "ATLAAVE":
break;
case "VENTAVE":
break;
case "WATERWORKS":
break;
case "MARVGARD":
break;
case "GOTOJAIL":
break;
case "PACAVE":
break;
case "NORTHAVE":
break;
case "PENNAVE":
break;
case "SHORTLINE":
break;
case "PARKPLACE":
break;
case "BOARDWALK":
break;
case "LUXTAX":
break;
}
yield return other;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playercontroller : MonoBehaviour
{
public List<GameObject> targets;
public float speed, step;
bool canmove = false;
public List<Camera> cameras;
public Camera diecam;
public List<GameObject> players;
public List<GameObject> playerreaders;
public int[] turntarget = new int[] { 0, 0, 0, 0 };
public int turn = 1;
int lastturn = 0;
bool switchit = true;
Coroutine inin;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(canmove)
{
if(!switchit || sharedscript.doubles)
{
foreach(GameObject s in playerreaders)
{
s.SetActive(false);
}
foreach (Camera s in cameras)
{
s.gameObject.SetActive(false);
}
foreach(GameObject s in players)
{
Collider g = s.GetComponent<Collider>();
g.enabled = false;
Rigidbody w = s.GetComponent<Rigidbody>();
w.isKinematic = true;
}
lastturn = turn;
switchit = true;
}
Collider d = players[turn].GetComponent<Collider>();
d.enabled = true;
Rigidbody f = players[turn].GetComponent<Rigidbody>();
f.isKinematic = false;
playerreaders[turn].SetActive(true);
cameras[turn].gameObject.SetActive(true);
diecam.gameObject.SetActive(false);
step = speed * Time.deltaTime;
if (players[turn].transform.position.x <= targets[turntarget[turn]].transform.position.x + .003 && players[turn].transform.position.x >= targets[turntarget[turn]].transform.position.x - .003 && players[turn].transform.position.z <= targets[turntarget[turn]].transform.position.z + .03 && players[turn].transform.position.z >= targets[turntarget[turn]].transform.position.z - .03)
{
if (turntarget[turn] < 3)
{
turntarget[turn] ++;
players[turn].transform.Rotate(0, 90, 0);
}
else
{
turntarget[turn] = 0;
}
}
players[turn].transform.position = Vector3.MoveTowards(players[turn].transform.position, targets[turntarget[turn]].transform.position, speed);
}
if(sharedscript.nummoved >= sharedscript.total)
{
canmove = false;
sharedscript.ismoving = false;
}
else
{
canmove = true;
sharedscript.ismoving = true;
}
if(sharedscript.nummoved >= sharedscript.total && sharedscript.total != 0 && sharedscript.nummoved != 0 && !sharedscript.doubles && switchit)
{
if (turn < 3)
turn++;
else
turn = 0;
sharedscript.reset = true;
switchit = false;
Debug.Log("playedturn");
sharedscript.canroll = true;
}
}
}
You are starting your coroutine whenever something enters the trigger collider. Try checking if the object that entered is the object you are looking for. If it is your player and the object is tagged "Player" you could check for that:
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
StartCoroutine(YourCoroutine());
}
}
Another reason could be Unity collision detection. It can be wonky at times so most of the time you are better off checking if the objects collided already.
One way to solve this issue using coroutines in particular would be to store the coroutine in a variable in your script and check if it already ran. Something like this:
private Coroutine coroutine;
private void OnTriggerEnter(Collider other)
{
if(coroutine == null)
{
coroutine = StartCoroutine(YourCoroutine());
}
}
Then set the value to null in OnTriggerExit or something:
private void OnTriggerExit(Collider other)
{
coroutine = null;
}
I would recommend to also check for the tag in this implementation:
private Coroutine coroutine;
private void OnTriggerEnter(Collider other)
{
if(coroutine == null && other.CompareTag("Player"))
{
coroutine = StartCoroutine(YourCoroutine());
}
}
Related
I am using this script to try and pull random noises to play during gameplay
public class Sons : MonoBehaviour
{
public static AudioClip al1, al2, al3, al4, al5, al6, al7, al8, al9, al10, al11, al12, al13;
static AudioSource Audio;
int randomizer;
// Start is called before the first frame update
void Start()
{
randomizer = (Random.Range(1, 13));
//sons aleatorios=============================================================
al1 = Resources.Load<AudioClip>("ran1");
al2 = Resources.Load<AudioClip>("ran2");
al3 = Resources.Load<AudioClip>("ran3");
al4 = Resources.Load<AudioClip>("ran4");
al5 = Resources.Load<AudioClip>("ran5");
al6 = Resources.Load<AudioClip>("ran6");
al7 = Resources.Load<AudioClip>("ran7");
al8 = Resources.Load<AudioClip>("ran8");
al9 = Resources.Load<AudioClip>("ran9");
al10 = Resources.Load<AudioClip>("ran10");
al11 = Resources.Load<AudioClip>("ran11");
al12 = Resources.Load<AudioClip>("ran12");
al13 = Resources.Load<AudioClip>("ran13");
InvokeRepeating("RNDSND", 1f, 20f);
//sons ventilação============================================================
//sons corredor==============================================================
//sons salas=================================================================
Audio = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
}
void RNDSND()
{
switch (randomizer)
{
case 1:
Audio.PlayOneShot(al1);
break;
case 2:
Audio.PlayOneShot(al2);
break;
case 3:
Audio.PlayOneShot(al3);
break;
case 4:
Audio.PlayOneShot(al4);
break;
case 5:
Audio.PlayOneShot(al5);
break;
case 6:
Audio.PlayOneShot(al6);
break;
case 7:
Audio.PlayOneShot(al7);
break;
case 8:
Audio.PlayOneShot(al8);
break;
case 9:
Audio.PlayOneShot(al9);
break;
case 10:
Audio.PlayOneShot(al10);
break;
case 11:
Audio.PlayOneShot(al11);
break;
case 12:
Audio.PlayOneShot(al12);
break;
case 13:
Audio.PlayOneShot(al13);
break;
}
}
}
...but unity sends the message "PlayOneShot Was Called With Null AudioClip"
Can anyone point out what I am doing wrong and any possible solutions?
I would recommend doing something more like this:
public class Sons : MonoBehaviour
{
public AudioClip[] randSounds;
static AudioSource Audio;
int randomizer;
// Start is called before the first frame update
void Start()
{
for(int i = 0; i < randSounds.Length; i++)
{
randSounds[i] = Resources.Load<AudioClip>("ran" + i);
}
Audio = GetComponent<AudioSource>();
InvokeRepeating("RNDSND", 1f, 20f);
}
void RNDSND()
{
randomizer = (Random.Range(0, randSounds.Length));
Audio.PlayOneShot(randSounds[randomizer]);
}
}
Untested. I would probably also just delete the Resources.Load call since it seems expensive memory wise to do that on load for all your objects, you probably want to assign those clips into the inspector if possible, or load them on Awake() and before you need to use them.
using UnityEngine;
using UnityEngine.SceneManagement;
public class CollisionHandler : MonoBehaviour
{
[SerializeField] AudioClip success;
[SerializeField] AudioClip crash;
[SerializeField] ParticleSystem successParicle;
[SerializeField] ParticleSystem crashParicle;
AudioSource audioSource;
float delayTime = 2f;
bool isTransitioning = false;
void Start()
{
audioSource = GetComponent<AudioSource>();
}
void OnCollisionEnter(Collision other)
{
if(isTransitioning)
{
return;
}
switch (other.gameObject.tag)
{
case "Friendly":
Debug.Log("It's OK friend");
break;
case "Finish":
SuccessSequence();
break;
default:
CrashSequence();
break;
}
void SuccessSequence()
{
isTransitioning = true;
audioSource.Stop();
successParicle.Play();
audioSource.PlayOneShot(success);
GetComponent<Shutle>().enabled = false;
Invoke(nameof(NextScene), delayTime);
//NextScene();
}
void CrashSequence()
{
isTransitioning = true;
audioSource.Stop();
crashParicle.Play();
audioSource.PlayOneShot(crash);
GetComponent<Shutle>().enabled = false;
Invoke(nameof(ReloadScene), delayTime);
//ReloadScene();
}
void ReloadScene() //dit gebruiken wij om respown met gebruik van scene reload funcite te gebruiken
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex);
}
void NextScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
int nextSceneIndex = currentSceneIndex + 1;
if(nextSceneIndex == SceneManager.sceneCountInBuildSettings)
{
nextSceneIndex = 0;
}
SceneManager.LoadScene(nextSceneIndex);
}
}
}
i am building a basic game where a ship flies from one place to the other. when it crashes or succeeds it should do a couple of functionality before it re-spawns or move on the next level. so i need to invoke the functions. whenever i try to do that i get the following message:
"Trying to Invoke method: CollisionHandler.NextScene couldn't be called."
Any good reason why your methods are all nested under the OnCollisionEnter as locel functions? ;)
Unity's messaging system doesn't find local functions - since they literally only exist for this one method - but works only with class level methods.
It should rather be
priate void OnCollisionEnter(Collision other)
{
if(isTransitioning)
{
return;
}
switch (other.gameObject.tag)
{
case "Friendly":
Debug.Log("It's OK friend");
break;
case "Finish":
SuccessSequence();
break;
default:
CrashSequence();
break;
}
}
priate void SuccessSequence()
{
isTransitioning = true;
audioSource.Stop();
successParicle.Play();
audioSource.PlayOneShot(success);
GetComponent<Shutle>().enabled = false;
Invoke(nameof(NextScene), delayTime);
//NextScene();
}
priate void CrashSequence()
{
isTransitioning = true;
audioSource.Stop();
crashParicle.Play();
audioSource.PlayOneShot(crash);
GetComponent<Shutle>().enabled = false;
Invoke(nameof(ReloadScene), delayTime);
//ReloadScene();
}
priate void ReloadScene() //dit gebruiken wij om respown met gebruik van scene reload funcite te gebruiken
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex);
}
priate void NextScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
int nextSceneIndex = currentSceneIndex + 1;
if(nextSceneIndex == SceneManager.sceneCountInBuildSettings)
{
nextSceneIndex = 0;
}
SceneManager.LoadScene(nextSceneIndex);
}
Alternatively to Invoke you could make it all routines which in my eyes is better to control, understand and maintain then "magic" Invoke calls ;)
priate IEnumerator OnCollisionEnter(Collision other)
{
if(isTransitioning)
{
yield break;
}
switch (other.gameObject.tag)
{
case "Friendly":
Debug.Log("It's OK friend");
break;
case "Finish":
yield return SuccessSequence();
break;
default:
yield return CrashSequence();
break;
}
}
priate IEnumerator SuccessSequence()
{
isTransitioning = true;
audioSource.Stop();
successParicle.Play();
audioSource.PlayOneShot(success);
GetComponent<Shutle>().enabled = false;
yield return new WaitForSeconds(delayTime);
NextScene();
}
priate IEnumerator CrashSequence()
{
isTransitioning = true;
audioSource.Stop();
crashParicle.Play();
audioSource.PlayOneShot(crash);
GetComponent<Shutle>().enabled = false;
yield return new WaitForSeconds(delayTime);
ReloadScene();
}
priate void ReloadScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex);
}
priate void NextScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
int nextSceneIndex = currentSceneIndex + 1;
if(nextSceneIndex == SceneManager.sceneCountInBuildSettings)
{
nextSceneIndex = 0;
}
SceneManager.LoadScene(nextSceneIndex);
}
So I am trying to create this script which changes the color of the sprite when I execute the method OnMouseDown().
The color change should execute every 2 seconds. In this code provided below the colors only change once for some reason.
I have already tried coroutines. But they didn't work for some reason.
Please help thanks,
public bool startstop = false;
SpriteRenderer m_SpriteRenderer;
IEnumerator Changecolor() {
yield return new WaitForSeconds(3);
int random = Random.Range(1, 4);
if (random == 1) {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.blue;
} else if (random == 2) {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.red;
} else if (random == 3) {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.green;
} else {
this.m_SpriteRenderer = this.GetComponent<SpriteRenderer>();
this.m_SpriteRenderer.color = Color.yellow;
}
this.StartCoroutine("Changecolor", 3f);
}
private void OnMouseDown() {
if (this.startstop) {
this.StartCoroutine("Changecolor", 3f);
this.startstop = !this.startstop;
} else {
this.StopCoroutine("Changecolor");
this.startstop = !this.startstop;
}
}
No errors just doesn't work.
Do you have a Collider on the object? A Collider is needed to have the OnMouseDown event fired.
The code principally works, but is far from quality.
- Only call GetComponent() once, then cache the result. This call is very expensive.
- Initially you have to click two times, as the Coroutine will be stopped at the first click.
Here is the code with some improvements:
public bool m_isRunning = false;
public SpriteRenderer m_spriteRenderer;
private void Start() {
m_spriteRenderer = this.GetComponent<SpriteRenderer>();
}
private IEnumerator Changecolor() {
yield return new WaitForSeconds(3);
int random = Random.Range(1, 4);
if (random == 1) {
m_spriteRenderer.color = Color.blue;
} else if (random == 2) {
m_spriteRenderer.color = Color.red;
} else if (random == 3) {
m_spriteRenderer.color = Color.green;
} else {
m_spriteRenderer.color = Color.yellow;
}
this.StartCoroutine("Changecolor", 3f);
}
private void OnMouseDown() {
m_isRunning = !m_isRunning;
if (m_isRunning) {
StartCoroutine("Changecolor", 3f);
} else {
StopCoroutine("Changecolor");
}
}
Note that the second parameter you pass in (3f)
StartCoroutine ("Changecolor", 3f);
does nothing since your Changecolor does not take any arguments ...
I would actually suggest to not use Coroutine here at all but rather InvokeRepeating and CancelInvoke
void Changecolor()
{
// Either in Awake or as lazy initialization
if(!m_SpriteRenderer) m_SpriteRenderer = GetComponent<SpriteRenderer>();
int random = Random.Range(1, 4);
switch(random)
{
case 1:
m_spriteRenderer.color = Color.blue;
break;
case 2:
m_spriteRenderer.color = Color.red;
break;
case 3:
m_spriteRenderer.color = Color.green;
break;
default:
m_spriteRenderer.color = Color.yellow;
break;
}
}
private void OnMouseDown()
{
startstop = !startstop;
if (this.startstop)
{
InvokeRepeating(nameof(Changecolor), 0f, 2f);
}
else
{
CancelInvoke(nameof(Changecolor));
}
}
The code you provided works just fine, and sins you say the color changes only once, I assume you have a collider on the object that has the script attached, the only thing you should change is the continues call to GetComponent<SpriteRenderer> because it is pretty costly and should only be called in either Start or Awake another thing, which isn't major, and there is nothing wrong with it, but it kind of rubs me the wrong way, is the creation of a new coroutine at the end of the old, why not do something like this:
private Coroutine _colorChanger;
private SpriteRenderer _renderer;
void Start() //Can be Awake, whichever you choose
{
_renderer = GetComponent<SpriteRenderer>();
if (_renderer == null)
{
Debug.Log("No sprite found.");
return;
}
//This is performed if OnMouseDown is implemented, if you implement the Update with Input.GetKeyDown, then this can be removed
var collider = GetComponent<Collider>();
if (collider == null)
{
collider = gameObject.AddComponent<BoxCollider>(); //or BoxCollider2D if you are applying the script to the sprite itself.
}
collider.isTrigger = true;
}
private void OnMouseDown() //this can be swapped out for what Saif wrote, a Update method which checks if the button is down, should be GetKeyDown instead of GetKey, having it that way will eliminate the need for a collider/UI element
{
if (_colorChanger == null)
{
_colorChanger = StartCoroutine(ChangeColor(2f));
}
else
{
StopCoroutine(_colorChanger);
_colorChanger = null;
}
}
IEnumerator ChangeColor(float timeoutSec)
{
while (true)
{
yield return new WaitForSeconds(timeoutSec);
int random = Random.Range(1, 5); //Change max from 4 to 5
if (random == 1)
{
_renderer.color = Color.blue;
}
else if (random == 2)
{
_renderer.color = Color.red;
}
else if (random == 3)
{
_renderer.color = Color.green;
}
else
{
_renderer.color = Color.yellow;
}
}
}
Update: just noticed something that others missed, you should change the Random.Range(1, 4) to Random.Range(1, 5) or else the yellow color will never come into effect.
Your code is right, no issues in it except that the OnMouseDown() function will not be called since you are not clicking on any box triggers or any UI elements. Hence try to use update function as given below:
void Update()
{
if (Input.GetKey(KeyCode.Mouse0))
{
if (startstop == false)
{
StartCoroutine("Changecolor", 3f);
this.startstop = !this.startstop;
}
else
{
StopCoroutine("Changecolor");
this.startstop = !this.startstop;
}
}
}
I'm trying to create a console game where character 'M' as in "Martian" and character 'S' as in "SpaceCreature" stay opposite on both ends on the X axis and move up and down across Y axis.
I use arrow keys to make the 'M' move up and down. But the 'S' should also move but by itself whenever 'M' moves. I need to make the 'S' move at a slower pace to follow the 'M'.
As of now, I got 'M' moving up and down using arrow keys and 'S' is also moving at the same time.
I need to make the 'S' move slower. I have tried thread.Sleep, but that just makes the 'S' disappear and appear back like a glitch. I think I need to use something called "Console.keyAvailable" but I am finding it hard on where to place that function.
//X and Y get set constructors are defined in the abstract class:-SpaceObject
public override void Draw() //In both classes Martian and SpaceCreature
{
Console.SetCursorPosition(X, Y);
Console.WriteLine("S");
//In Martian class:- Console.WriteLine("M");
}
static void Main(string[] args)
{
var m = new Martian(100, 10);
var s = new SpaceShip(100, 10);
const int MaxY = 25;
m.Draw(); //Abstract override void method
s.X = m.X + 100;
s.Y = m.Y;
s.Draw(); //Abstract override void method
ConsoleKeyInfo keyInfo;
while (true)
{
keyInfo = Console.ReadKey(true);
Console.Clear();
switch (keyInfo.Key)
{
case ConsoleKey.UpArrow:
if (m.Y > 0)
{
m.Y--;
}
break;
case ConsoleKey.DownArrow:
if (m.Y < MaxY)
{
m.Y++;
}
break;
}
m.Draw();
s.X = m.X + 100;
s.Y = m.Y;
s.Draw();
}
}
}
You don't need another thread...play with this. Press the up/down arrows or escape to quit; you do NOT have to hold down the arrow keys for continued movement. You may also be interested in my Console Snake example.
class Program
{
enum Directions
{
Up,
Down,
None
}
static void Main(string[] args)
{
DateTime next;
bool quit = false;
ConsoleKeyInfo cki;
Directions direction = Directions.None;
Console.Clear();
Console.CursorVisible = false;
var m = new Martian();
var s = new SpaceShip();
m.Draw(true);
s.Draw(true);
do
{
// wait for next keypress, or next movement
next = new DateTime(Math.Min(m.nextMovement.Ticks, s.nextMovement.Ticks));
while(!Console.KeyAvailable && DateTime.Now < next)
{
System.Threading.Thread.Sleep(10);
}
// was a key pressed?
if (Console.KeyAvailable)
{
cki = Console.ReadKey(true);
switch (cki.Key)
{
case ConsoleKey.UpArrow:
direction = Directions.Up;
break;
case ConsoleKey.DownArrow:
direction = Directions.Down;
break;
case ConsoleKey.Escape:
quit = true;
break;
}
}
// does anything need to move?
if (DateTime.Now >= m.nextMovement)
{
switch(direction)
{
case Directions.Up:
m.MoveUp();
break;
case Directions.Down:
m.MoveDown();
break;
case Directions.None:
m.UpdateNextMovement();
break;
}
}
if (DateTime.Now >= s.nextMovement)
{
s.MoveToward(m);
}
} while (!quit);
}
}
public abstract class SpaceObject
{
public int X;
public int Y;
public int MovementDelay;
public DateTime nextMovement;
abstract public void Draw(bool Visible);
public void MoveUp()
{
if (this.Y > 0)
{
this.Draw(false);
this.Y--;
this.Draw(true);
}
this.UpdateNextMovement();
}
public void MoveDown()
{
if (this.Y < Console.WindowHeight - 1)
{
this.Draw(false);
this.Y++;
this.Draw(true);
}
this.UpdateNextMovement();
}
public void MoveToward(SpaceObject so)
{
if (so.Y < this.Y)
{
this.MoveUp();
}
else if (so.Y > this.Y)
{
this.MoveDown();
}
else
{
this.UpdateNextMovement();
}
}
public void UpdateNextMovement()
{
this.nextMovement = DateTime.Now.AddMilliseconds(this.MovementDelay);
}
}
public class Martian : SpaceObject
{
public Martian()
{
this.X = 1;
this.Y = Console.WindowHeight / 2;
this.MovementDelay = 100;
this.nextMovement = DateTime.Now.AddMilliseconds(this.MovementDelay);
}
public override void Draw(bool Visible)
{
Console.SetCursorPosition(this.X, this.Y);
Console.Write(Visible ? "M" : " ");
}
}
public class SpaceShip : SpaceObject
{
public SpaceShip()
{
this.X = Console.WindowWidth - 2;
this.Y = Console.WindowHeight / 2;
this.MovementDelay = 750;
this.nextMovement = DateTime.Now.AddMilliseconds(this.MovementDelay);
}
public override void Draw(bool Visible)
{
Console.SetCursorPosition(this.X, this.Y);
Console.Write(Visible ? "S" : " ");
}
}
----- EDIT -----
How do I make the 'M' movement by tapping up/ down arrow keys instead
of making continuous movement?
Change the "was a key pressed" block to:
// was a key pressed?
if (Console.KeyAvailable)
{
cki = Console.ReadKey(true);
switch (cki.Key)
{
case ConsoleKey.UpArrow:
m.MoveUp();
break;
case ConsoleKey.DownArrow:
m.MoveDown();
break;
case ConsoleKey.Escape:
quit = true;
break;
}
}
Then delete the if (DateTime.Now >= m.nextMovement) block so you are only left checking for the SpaceShip time below. Now your "M" should only move when you tap and/or hold down the arrow keys.
Did you try to put Thread.Sleep(100); right after s.Y = m.Y; and didn't work?
Changing sleep time to shorter times might work.
Also:
while (true)
{
keyInfo = Console.ReadKey(true);
Console.Clear();
switch (keyInfo.Key)
{
case ConsoleKey.UpArrow:
if (m.Y > 0)
{
m.Y--;
}
break;
case ConsoleKey.DownArrow:
if (m.Y < MaxY)
{
m.Y++;
}
break;
}
}
m.Draw();
s.X = m.X + 100;
s.Y = m.Y;
s.Draw(); //i think is better to put draw functions outside switch(key)
}
I'm trying out some code for an idea I had recently. But right now I'm stuck on trying to find a good way to add a delay. I've been trying to use coroutine, and I get a delay, but the method called after that gets called way too many times (I only want it to be called once)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using System;
using Random = UnityEngine.Random;
public class YourHealth : MonoBehaviour
{
bool yourTurn = true;
public Button button1;
public Button button2;
public Text YH;
public Text EH;
private int yourHealth = 100;
private int enemyHealth = 100;
// Use this for initialization
void Start()
{
button1.onClick.AddListener(Heal20);
button2.onClick.AddListener(Damage40);
YH.text = Convert.ToString(yourHealth);
EH.text = Convert.ToString(enemyHealth);
}
public void Heal20()
{
yourHealth += 20;
yourTurn = false;
}
public void Damage40()
{
enemyHealth -= 40;
yourHealth -= 5;
yourTurn = false;
}
public void Update()
{
YH.text = Convert.ToString(yourHealth);
EH.text = Convert.ToString(enemyHealth);
if (yourTurn == false)
{
button1.interactable = false;
StartCoroutine(Wait(2));
}
else
{
button1.interactable = true;
}
}
public void EnemyTurn()
{
int roll = Random.Range(1 , 7);
switch (roll)
{
case 1:
yourHealth -= 10;
break;
case 2:
enemyHealth -= 10;
break;
case 3:
yourHealth -= 30;
break;
case 4:
yourHealth += 5;
break;
case 5:
break;
case 6:
enemyHealth += 10;
break;
}
yourTurn = true;
}
IEnumerator Wait(float time)
{
yield return new WaitForSecondsRealtime(time);
EnemyTurn();
}
}
I want EnemyTurn to be called only once after the delay. But instead it runs a bunch of times. I hope I can get a solution to this quickly because it has just brought a halt to my project and I can't figure it out myself.
That's because you're creating a Wait Coroutine on every Update call between the player's end and the enemy's turn. You need a way to only create it once per player turn end.
One way is adding a flag that you set when you create the Wait Coroutine and then unset it when the enemy's turn is done:
// If this is true, we are already waiting
// and don't want more Wait coroutines (yet)
private bool waiting;
void Start()
{
button1.onClick.AddListener(Heal20);
button2.onClick.AddListener(Damage40);
YH.text = Convert.ToString(yourHealth);
EH.text = Convert.ToString(enemyHealth);
waiting = false;
}
// ...
if (yourTurn == false)
{
if (!waiting)
{
// we only want to do this stuff the first frame we start waiting
button1.interactable = false;
waiting = true;
StartCoroutine(Wait(2));
}
}
else
{
button1.interactable = true;
}
// ...
IEnumerator Wait(float time)
{
yield return new WaitForSecondsRealtime(time);
EnemyTurn();
waiting = false;
}