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);
}
Related
I have a script to instantiate a gameobject when the agent reaches its position, when instantiated it will randomly choose any of those 2, then 3, etc... this gameobjects belong to a parent (I use the random with childcount and getChild), however my agent won't move to the instances, just the original one. I've tried using it's position and it's localPosition and none of them works, actually if I use the local position it won't even do the set destination to the original one. I can't guess if its a navmesh problem or if there is an error with my scripting. I can add some images so if anyone can help me.
Script part:
if (plant_gameobject_father.transform.childCount != 0)
{
chosen_child = Random.Range(0, plant_gameobject_father.transform.childCount - 1);
plant_target = plant_gameobject_father.transform.GetChild(chosen_child);
Debug.Log(plant_target.transform.position);
agent_.SetDestination(plant_target.position);
Debug.Log(agent_.pathStatus);
}
Video Sample:
Navmesh working just on the original gameobject
EDIT: When using agent.remainingDistance to check how it's doing it: right after assigning the destination the remaining distance is 0, and when it "arrives" to the target it's remaining distance it's bigger than it should (I have a coroutine using yield return wait until agent.remainingDistance < 3.5f) and still it thinks it has reached destination.
I will upload full script for context understanding(it's a long one)
Could it be that the distances are too big? Mi terrain is larger that 2000 units in lenght.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class insect_IA : MonoBehaviour
{
public ciclo_dia_noche info_horas; //Programado para que haga 3 ciclos de actividad al dia y inactividad de noche.
public GameObject plant_type_small;
public GameObject plant_type_medium;
public GameObject plant_type_big;
Transform plant_target;
GameObject plant_gameobject_father;
public GameObject colmena;
public int objetivo_diario_recoleccion;
public int segundos_recoleccion_polen;
public int segundos_depositado_polen;
public int plant_selector;
public NavMeshAgent agent_;
int selector_accion;
public bool wander_is_happening;
public GameObject colmena_wander_childs;
public Transform wander_target;
public int is_performing_routine_insect;
public bool its_a_new_day;
public birth_controller puedo_reproducir;
public Collider tree_spawner1, tree_spawner2, tree_spawner3, selected_collider;
public Vector3 bounds_max, bounds_min;
public int random_number;
public float spawn_point_x, spawnpoint_z, spawnpoint_y;
public Vector3 spawn_point_tree;
public GameObject tree_big_prefab, tree_med_prefab, tree_peq_prefab;
public int chosen_child;
// Start is called before the first frame update
void Start()
{
}
private void Awake()
{
info_horas = GameObject.Find("Directional_Light").GetComponent<ciclo_dia_noche>();
plant_type_big = GameObject.Find("PLANTAS_GRAND");
plant_type_medium = GameObject.Find("PLANTAS_MED");
plant_type_small = GameObject.Find("PLANTAS_PEQ");
colmena = GameObject.Find("colmena");
puedo_reproducir = GameObject.Find("birth_controlator").GetComponent<birth_controller>();
tree_spawner1 = GameObject.Find("spawn_area1").GetComponent<Collider>();
tree_spawner2 = GameObject.Find("spawn_area2").GetComponent<Collider>();
tree_spawner3 = GameObject.Find("spawn_area3").GetComponent<Collider>();
tree_big_prefab = GameObject.Find("PLANTAS_GRAND/planta_grand");
tree_med_prefab = GameObject.Find("PLANTAS_MED/planta_med");
tree_peq_prefab = GameObject.Find("PLANTAS_PEQ/planta_peq");
agent_ = GetComponent<NavMeshAgent>();
colmena_wander_childs = colmena;
selector_accion = 0;
segundos_recoleccion_polen = 5;
segundos_depositado_polen = 5;
objetivo_diario_recoleccion = 3;
is_performing_routine_insect = 0;
its_a_new_day = true;
random_number = -1;
}
// Update is called once per frame
void Update()
{
if ((puedo_reproducir.plant_big_type.transform.childCount < puedo_reproducir.max_plant_big || puedo_reproducir.plant_med_type.transform.childCount < puedo_reproducir.max_plant_med || puedo_reproducir.plant_peq_type.transform.childCount < puedo_reproducir.max_plant_peq) && info_horas.segundos_globales < info_horas.duracion_dia)
{
insect_state();
}
if (wander_is_happening == false && puedo_reproducir.plant_big_type.transform.childCount > puedo_reproducir.max_plant_big && puedo_reproducir.plant_med_type.transform.childCount > puedo_reproducir.max_plant_med && puedo_reproducir.plant_peq_type.transform.childCount > puedo_reproducir.max_plant_peq)
{
wander_is_happening = true;
is_performing_routine_insect = 4;
StartCoroutine("regular_wander");
}
}
public void insect_state()
{
if (selector_accion == 0)
{
//Debug.Log("buscar padre arbol");
is_performing_routine_insect = 1;
selector_accion = -1;
cojo_un_padre_arbol();
selector_accion = 1;
}
if (selector_accion == 1)
{
//Debug.Log("elegir destino");
selector_accion = -2;
elijo_destino();
selector_accion = 2;
}
if (selector_accion == 2)
{
//Debug.Log("esperar a que llegue");
selector_accion = -3;
check_path();
}
if (selector_accion == 3)
{
//Debug.Log("cogiendo polen");
is_performing_routine_insect = 2;
StartCoroutine("cogiendo_polen");
}
if (selector_accion == 4)
{
//Debug.Log("de vuelta a la colmena");
selector_accion = -5;
volver_colmena();
check_path();
}
if (selector_accion == 5)
{
//Debug.Log("guardo polen");
is_performing_routine_insect = 3;
StartCoroutine("guardando_polen");
}
if (selector_accion == 6)
{
//Debug.Log("reinicio insecto");
is_performing_routine_insect = 4;
StartCoroutine("esperar_proxima_recoleccion");
}
}
public void cojo_un_padre_arbol()
{
if (puedo_reproducir.plant_big_type.transform.childCount < puedo_reproducir.max_plant_big)
{
plant_selector = 2;
}
else if (puedo_reproducir.plant_med_type.transform.childCount < puedo_reproducir.max_plant_med)
{
plant_selector = 1;
}
else if (puedo_reproducir.plant_peq_type.transform.childCount < puedo_reproducir.max_plant_peq)
{
plant_selector = 0;
}
if (plant_selector == 0)
{
plant_gameobject_father = plant_type_small;
}
if (plant_selector == 1)
{
plant_gameobject_father = plant_type_medium;
}
if (plant_selector == 2)
{
plant_gameobject_father = plant_type_big;
}
//Debug.Log("padre elegido:" + plant_gameobject_father);
}
public void elijo_destino()
{
if (plant_gameobject_father.transform.childCount != 0)
{
chosen_child = Random.Range(0, plant_gameobject_father.transform.childCount - 1);
plant_target = plant_gameobject_father.transform.GetChild(chosen_child);
Debug.Log(plant_target.transform.position);
agent_.SetDestination(plant_target.position);
Debug.Log(agent_.pathStatus);
}
else if(plant_gameobject_father.transform.childCount == 0)
{
wander_is_happening = true;
is_performing_routine_insect = 4;
StartCoroutine("regular_wander");
}
//Debug.Log(this.transform.position);
//Debug.Log("planta seleccionada: " + plant_target);
//Debug.Log(agent_.remainingDistance);
}
public void check_path()
{
StartCoroutine("esperar_destino");
}
public void volver_colmena()
{
agent_.SetDestination(colmena.transform.position);
create_plant();
}
public IEnumerator cogiendo_polen()
{
selector_accion = -4;
yield return new WaitForSeconds(segundos_recoleccion_polen);
selector_accion = 4;
}
public IEnumerator guardando_polen()
{
selector_accion = -6;
yield return new WaitForSeconds(segundos_depositado_polen);
selector_accion = 6;
}
public IEnumerator esperar_destino()
{
if (plant_target.tag == "planta_peq")
{
//Debug.Log(agent_.remainingDistance);
yield return new WaitUntil(() => agent_.remainingDistance < 1.7f);
//Debug.Log(agent_.remainingDistance);
agent_.isStopped = true;
agent_.ResetPath();
if (selector_accion == -3)
{
selector_accion = 3;
}
if (selector_accion == -5)
{
selector_accion = 5;
}
}
if (plant_target.tag == "planta_med")
{
//Debug.Log(agent_.remainingDistance);
yield return new WaitUntil(() => agent_.remainingDistance < 3.0f);
//Debug.Log(agent_.remainingDistance);
agent_.isStopped = true;
agent_.ResetPath();
if (selector_accion == -3)
{
selector_accion = 3;
}
if (selector_accion == -5)
{
selector_accion = 5;
}
}
if (plant_target.tag == "planta_grand")
{
Debug.Log(agent_.remainingDistance);
yield return new WaitUntil(() => agent_.remainingDistance < 3.5f);
Debug.Log(agent_.remainingDistance);
agent_.isStopped = true;
agent_.ResetPath();
if (selector_accion == -3)
{
selector_accion = 3;
}
if (selector_accion == -5)
{
selector_accion = 5;
}
}
}
public IEnumerator esperar_proxima_recoleccion()
{
selector_accion = -7;
yield return new WaitForSeconds(1);
selector_accion = 0;
}
public IEnumerator regular_wander()
{
wander_target = colmena_wander_childs.transform.GetChild(Random.Range(0, colmena_wander_childs.transform.childCount - 1));
agent_.SetDestination(wander_target.position);
yield return new WaitUntil(() => agent_.remainingDistance < 0.1f);
agent_.isStopped = true;
agent_.ResetPath();
yield return new WaitForSeconds(5);
wander_is_happening = false;
}
public void create_plant()
{
//Debug.Log("entro a crear una planta");
random_number = Random.Range(0, 3);
if (random_number == 0)
{
selected_collider = tree_spawner1;
}
if (random_number == 1)
{
selected_collider = tree_spawner2;
}
if (random_number == 2)
{
selected_collider = tree_spawner3;
}
bounds_max = selected_collider.bounds.max;
bounds_min = selected_collider.bounds.min;
spawn_point_x = Random.Range(bounds_min.x, bounds_max.x);
spawnpoint_z = Random.Range(bounds_min.z, bounds_max.z);
spawnpoint_y = bounds_max.y;
spawn_point_tree = new Vector3(spawn_point_x, spawnpoint_y, spawnpoint_z);
if (plant_target.tag == "planta_peq")
{
Instantiate(tree_peq_prefab, spawn_point_tree, Quaternion.identity, plant_type_big.transform);
}
if (plant_target.tag == "planta_med")
{
Instantiate(tree_med_prefab, spawn_point_tree, Quaternion.identity, plant_type_medium.transform);
}
if (plant_target.tag == "planta_grand")
{
Instantiate(tree_big_prefab, spawn_point_tree, Quaternion.identity, plant_type_big.transform);
}
Debug.Log(puedo_reproducir.plant_big_type.transform.childCount);
}
}
ยดยดยดยด
There's a lot going on in your script. I'm going to put some calculated guesses here on what might help.
Your birth_controller object seems to have a separate list of all the small/medium/big plants. Are you 100% sure these reference the same as the ones in your Insect_IA object?
You are also using a lot of logic that seems very framerate dependant. Your update function itself calls insect_state() which functions as a state machine that runs through one step per frame, one frame at a time. At the same time, you start coroutines that work alongside that during all that. selector_accion is edited in both insect_state and a lot of those coroutines. Are you sure you're not deadlocking yourself out of certain values of selector_accion which are necessary for the navmesh to work?
Also: Random.Range(int minInclusive, int maxExclusive) is, for the int overload, EXCLUSIVE on the upper bound, so you can remove the -1 from plant_gameobject_father.transform.childCount - 1.
EDIT:
As discussed in the comments and in edits to the original post, the scale of your scene might be causing issues as well. If your navmesh agent cannot physically get as close as your yield WaitUntil statements are waiting for, that's probably deadlocking your logic.
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)
}
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());
}
}
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;
}