Something to replace Thread.Sleep in a snake game - c#

I am making a snake game as my homework assignment , I've already added a code for making the snake head move depending on the user input (left , right , down , up)
But I am stuck with the timing of it , I used Thread.Sleep in order for the game not to crash and get an exception , but my instructor told me that Thread.Sleep is a horrible idea in programming because it literally adds a delay to your game.
So, I need to somehow make it that there won't be a delay in my game while avoiding Thread.Sleep
class Program
{
static void Main(string[] args)
{
Direction direction = Direction.Down;
Console.CursorVisible = false;
int x=0 , y=0 ;
int xprev = 2, yprev = 2;
char shape = 'o';
x = xprev;
y = yprev;
Console.SetCursorPosition(xprev, yprev);
Console.Write(shape);
UserInput input = new UserInput();
ConsoleKeyInfo? info;
while (true)
{
info = input.GetKey();
if (info.HasValue)
{
switch (info.Value.Key)
{
case ConsoleKey.RightArrow:
direction = Direction.Right;
break;
case ConsoleKey.LeftArrow:
direction = Direction.Left;
break;
case ConsoleKey.UpArrow:
direction = Direction.Up;
break;
case ConsoleKey.DownArrow:
direction = Direction.Down;
break;
}
}
Thread.Sleep(100);
switch (direction)
{
case Direction.Up:
y--;
break;
case Direction.Down:
y++;
break;
case Direction.Left:
x--;
break;
case Direction.Right:
x++;
break;
}
Console.MoveBufferArea(xprev, yprev, 1, 1, x, y);
xprev = x;
yprev = y;
}

As Sohaib Jundi suggests, a timer of some description is a reasonable solution here. Your objective is:
Every 100ms, update where the snake is
Rather than solve this by saying make the application pause for 100ms, then update the snake, using a timer changes it to Have something that triggers every 100ms to update the snake.
For example:
using System;
using System.Threading;
namespace Snake
{
class Program
{
static void Main(string[] args)
{
var snakeTimer = new Timer(updateTheSnake, null, 0, 100);
while(true)
{
var keypress = Console.ReadKey();
if (keypress.Key == ConsoleKey.Escape)
{
break;
}
}
}
static void updateTheSnake(object state)
{
Console.Write("#");
}
}
}
This is a very simple example that just draws a line of # across the screen until the user presses the escape key. In your code, you'd likely want to move everything below the Thread.Sleep into the updateTheSnake method. direction will probably need to be stored into shared state so that you can refer to it from the updateTheSnake method.

Related

How to make difference between click with moving with click without moving gameobject in unity

I have 2 2D gameobjects in unity.I want to achieve next but only with Input.GetTouch for android:
When i click on first or second object---play some sound.
When drag any of those game objets to each other combine them to one---without playing sound from first case.
Then be able to click on this combined object---without playing sound from first case
Problem is because I dont want to play sound from first case if object is dragging.
I already tried some code with couroutine but it doesnt work:
if (Input.touchCount > 0) {
touch = Input.GetTouch(0);
touchPos = Camera.main.ScreenToWorldPoint(touch.position);
RaycastHit2D hit = Physics2D.Raycast(touchPos, Camera.main.transform.forward);
switch (touch.phase) {
case TouchPhase.Began:
samoKlik = true;
if (samoKlik) {
StartCoroutine(korotinaKlika());
}
}
case TouchPhase.Moved:
isMoved = true;
if (!samoKlik) {
if (jedan) {
break;
case TouchPhase.Ended:
isMoved = false;
jedan = false;
break;
}
You need to write your switch case properly. This code will not compile. The proper switch case syntac would be somethink like the one described here:
switch (touch.phase) {
case TouchPhase.Began:
samoKlik = true;
//if (samoKlik) { // This is not needed
StartCoroutine(korotinaKlika());
//}
break;
case TouchPhase.Moved:
isMoved = true;
if (!samoKlik) { // samoKlik does not exist here
if (jedan) {
break;
}
}
break;
case TouchPhase.Ended:
isMoved = false;
jedan = false;
break;
}
Since you want to fall from one conditional to another, it looks like the switch statement is not right for you.
I would suggest to try the following:
Declare your variables before your logic
bool samoKlik = false;
bool isMoved = false;
bool jedan = false;
Start with if/else clauses.
I can identify one for you to help you going
if (touch.phase == touchPhase.Moved && !samoKlik && jedan)

How do I use a timer/ delay to make an object or a character move in Console Window?

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)
}

Wait for coroutine to finish [duplicate]

This question already has an answer here:
More organized way to call Coroutines?
(1 answer)
Closed 5 years ago.
I have coroutine that after N seconds clear text and returns it to it's original shape. Problem is that coroutine never continues after first return (wait for seconds).
I had this problem somewhere else and figured it out that is happening because i destroy Gameobject before coroutine finish so i made it return bool but now i am confused and can not use same trick here since i am starting coroutine through script that is not initialized. That script only has static function through which i start coroutine. Here is my code:
void OnMouseDown()
{
bool safeDestroy = false;
IGatherable gather = CharacterCommands.character.GetComponent<IGatherable>();
if(gather != null)
{
switch(itemID)
{
case 3:
Drops[] d = ChestDrop.GetItemFromDropStash(drops, gather, this); //Here is function that is starting function with coroutine PROBLEM
if(d.Length == 0)
{
safeDestroy = true;
}
else
{
drops = d;
}
break;
default:
if(ItemDatabase.GetItem(itemID).maxStackable < Inventory.GetCoins() + amount)
{
Parameters.centerText.text = "Not enough space in your bag!";
safeDestroy = Parameters.clearText(Parameters.centerText, 3, this); //Coroutine i had same problem but done it this way.
}
else
{
gather.GatherItem(itemID, amount);
safeDestroy = true;
}
break;
}
}
if(safeDestroy)
{
Destroy(this.gameObject);
}
}
And here is the function itself:
public static Drops[] GetItemFromDropStash(Drops[] drops, IGatherable gather, MonoBehaviour justToStartCoroutine)
{
foreach(Drops drop in drops)
{
int r = UnityEngine.Random.Range(1, 101);
if(r < drop.chance)
{
if(ItemDatabase.GetItem(drop.itemID).maxStackable > Inventory.GetItemFromInventoryById(drop.itemID).amount + drop.amount)
{
Inventory.AddItemToInventory(drop.itemID, drop.amount);
Parameters.centerText.text = "+" + drop.amount + " " + ItemDatabase.GetItem(drop.itemID).itemName;
switch(ItemDatabase.GetItem(drop.itemID).itemRarity)
{
case ItemRarity.common:
Parameters.centerText.color = Color.gray;
break;
case ItemRarity.normal:
Parameters.centerText.color = new Color(80, 100, 255);
break;
case ItemRarity.rare:
Parameters.centerText.color = new Color(255, 80, 80);
break;
case ItemRarity.special:
Parameters.centerText.color = new Color(200, 0, 220);
break;
case ItemRarity.legacy:
Parameters.centerText.color = new Color(199, 224, 0);
break;
case ItemRarity.legendary:
Parameters.centerText.color = new Color(224, 169, 0);
break;
}
bool t = Parameters.clearText(Parameters.centerText, 3, justToStartCoroutine);
int i = Array.IndexOf(drops, drop);
List<Drops> tmp = new List<Drops>(drops);
tmp.RemoveAt(i);
drops = tmp.ToArray();
}
else if (Inventory.CheckForFreeSpaceInInventory() == true)
{
Inventory.AddItemToInventoryToNewSlot(drop.itemID, drop.amount);
Parameters.centerText.text = "+" + drop.amount + " " + ItemDatabase.GetItem(drop.itemID).itemName;
switch(ItemDatabase.GetItem(drop.itemID).itemRarity)
{
case ItemRarity.common:
Parameters.centerText.color = Color.gray;
break;
case ItemRarity.normal:
Parameters.centerText.color = new Color(80, 100, 255);
break;
case ItemRarity.rare:
Parameters.centerText.color = new Color(255, 80, 80);
break;
case ItemRarity.special:
Parameters.centerText.color = new Color(200, 0, 220);
break;
case ItemRarity.legacy:
Parameters.centerText.color = new Color(199, 224, 0);
break;
case ItemRarity.legendary:
Parameters.centerText.color = new Color(224, 169, 0);
break;
}
bool t = Parameters.clearText(Parameters.centerText, 3, justToStartCoroutine);
int i = Array.IndexOf(drops, drop);
List<Drops> tmp = new List<Drops>(drops);
tmp.RemoveAt(i);
drops = tmp.ToArray();
}
else
{
Parameters.centerText.text = "Not enough space in inventory!";
bool t = Parameters.clearText(Parameters.centerText, 3, justToStartCoroutine);
}
}
}
return drops;
}
How can i achieve so that my item (where OnMouseDown() is) doesn't destroy until coroutine is finished?
If you want to wait for a coroutine to finish...then the method must also be a coroutine.
You have 3 options, depending on your specific scenario:
Move the code that needs to wait into the existing coroutine method (after the last current yield.
You can also pass a delegate into the current coroutine and use it as a callback, then each use of the existing coroutine can supply its own callback delegate. The "wait for finish" code then goes inside this delegate.
Turn the current not-coroutine method into a coroutine.
Create a new coroutine, shove the code that needs to wait for the other coroutine into it, including the call to the existing coroutine, yield on that other coroutine.

Program skipping stages

So I have a Unity program, where it switches screens if the user presses ENTER.
However, whenever the user presses enter, instead of just going one screen forward, it skips all the way to the last screen.
Here is my OnGUI():
if (showScreen == true)
{
//this allows the screens to toggle
switch (pageNum)
{
case 1:
rectangle = GUI.Window(1,rectangle, Page1, "Computer");
break;
case 2:
rectangle = GUI.Window(1,rectangle, Page2, "Computer");
break;
case 3:
rectangle = GUI.Window(1,rectangle, Page3, "Computer");
break;
case 4:
rectangle = GUI.Window(1,rectangle, Page4, "Computer");
break;
case 5:
rectangle = GUI.Window(1,rectangle, Page5, "Computer");
GUILayout.Window (2,rect2, popUp, "Warning!");
break;
case 6:
rectangle = GUI.Window(1,rectangle, Page6, "Computer");
break;
case 7:
rectangle = GUILayout.Window(1,rectangle, Page7, "Computer");
break;
default:
break;
}
}
And here is where the GUIs are actually set up, and made to change:
void Page1(int windowID)
{ if (Input.GetKeyDown(KeyCode.Return))
{
GUI.FocusControl(null);
pageNum = 2;
}
}
void Page2(int windowID)
{
if (Input.GetKeyDown(KeyCode.Return))
{
pageNum = 3;
GUI.FocusControl(null);
}
}
//continue like this for 5 more Page#(int windowID)
So what can I insert into these classes to make my program stop skipping? should I put a pause into these if statements too?
So it looks like the easiest way to solve this problem is by changing how the program changes the stages. All you need to do is a boolean that changes to true once the return key has been pressed.
so just declare the boolean:
public class ClassName : MonoBehaviour
{
bool wasClicked = false;
Then to change the states just do this:
if (Input.GetKey(KeyCode.Return))
{
wasClicked = true;
}
if (wasClicked)
{
if (!Input.GetKey(KeyCode.Return))
{
GUI.FocusControl(null);
pageNum = next page number;
wasClicked = false;
}
}

Problem to move many images in c#

I wanna write an application to do below job:
We have 15 different location (that we present them with point (x1,y1) for first location, point (x1,y2) for second location and so on point (x1,y15) for last location).
100 image must to be shown at this locations (with specific order) with pictureBox.
Each image that shown, must start to move horizontally to reach new point (e.g. point (Xc, Yc) ) and then move vertically and so on…
When the image reach to specific point (e.g. point (Xm,Ym) ), we decide that it must continue moving or it must destroy (with probability 20%) (which means that we can create next image at that initial location).
For example, an image create at location (Xi,Yi) with pictureBox1. Then it’s not allowed to create any more image at location (Xi,Yi) until pictureBox1 destroy or return to its initial location.
What I have written so far is:
create 15 location.
move one image to reach point (Xc,Yc).
I’ve some problems:
I use one timer to move an image. But I want to move 100 images. (from each 15 location, we create images and move them until we destroy them and then create next image).
So what I must to do? Use 15 timer for each location?!but How?
at specific point (e.g. point (Xk,Yk) ), the image must stop moving for random seconds and then continue moving. How I should do this? With another timer?!But how?
when image reach point (Xc,Yc) and with we decide to destroy it or not, nothing happen in my code…I don’t know why!
I’ve add an picture of what I want HERE
Here is my code so far:
public partial class Form1 : Form
{
Random r = new Random();
// falg to prevent create just one image at each location
private Boolean[] createNext = {true,true,true,true,true,true,true,
true,true,true,true,true,true,true,true};
private void setImage()
{
int i = 1 + r.Next() % 15;
while (createNext[i] != true)
i = 1 + r.Next() % 15;
switch (i)
{
case 1: pictureBox1.ImageLocation = "grin.png";
break;
case 2: pictureBox2.ImageLocation = "grin.png";
break;
case 3: pictureBox3.ImageLocation = "grin.png";
break;
case 4: pictureBox4.ImageLocation = "grin.png";
break;
case 5: pictureBox5.ImageLocation = "grin.png";
break;
case 6: pictureBox6.ImageLocation = "grin.png";
break;
case 7: pictureBox7.ImageLocation = "grin.png";
break;
case 8: pictureBox8.ImageLocation = "grin.png";
break;
case 9: pictureBox9.ImageLocation = "grin.png";
break;
case 10: pictureBox10.ImageLocation = "grin.png";
break;
case 11: pictureBox11.ImageLocation = "grin.png";
break;
case 12: pictureBox12.ImageLocation = "grin.png";
break;
case 13: pictureBox13.ImageLocation = "grin.png";
break;
case 14: pictureBox14.ImageLocation = "grin.png";
break;
case 15: pictureBox15.ImageLocation = "grin.png";
break;
}
}
private int k = 0;
private Boolean destroy = true;
// timer that move pictureBox1
void timer_Tick(object sender, EventArgs e)
{
k++;
int x = pictureBox1.Location.X;
int y = pictureBox1.Location.Y;
if (k <= 150)//go right 300 in 150 ticks
pictureBox1.Location = new Point(x + 2, y);
else if (k <= 300)
{
if (y < 200)
pictureBox1.Location = new Point(x, y + 1);
if (y > 200)
pictureBox1.Location = new Point(x, y - 1);
}
else if (k <= 400)
pictureBox1.Location = new Point(x + 2, y);
else if (k <= 550)
{
if (destroy == false)
pictureBox1.Location = new Point(x + 2, y);
if (destroy == true)
pictureBox1.Location = new Point(x, y - 3);
}
else if (k <= 650)
pictureBox1.Location = new Point(x, y - 1);
else if (k <= 850)
pictureBox1.Location = new Point(x - 2, y);
else if (k <= 950)
pictureBox1.Location = new Point(x, y + 1);
else
timer1.Stop();
}
public Form1()
{
InitializeComponent();
for (int i = 0; i < 15; i++)
{
setImage();
timer1.Start();
timer1.Interval = 15;
timer1.Tick += new EventHandler(timer_Tick);
}
}
Please help me to complete this application.
Thanks in advance.
You should probably just use one timer, and make a number of instances of a class that represents your moving picturebox. (This class would contain the points it will go to, ordered to the first point is always your next destination. Also it will contain the picturebox.. etc).
enum Action
{
None,
Stop,
CheckDestroy
}
class InterestingPoint
{
Point m_Point;
Action m_Action;
}
class MovingImage
{
public bool DestroyMe {get; private set; }
PictureBox m_PictureBox;
List<InterestingPoint> m_PointsToVisit;
int m_StoppedUntil = 0;
MovingImage(...)
{
// Constructor
}
void Update(int k)
{
if(m_StoppedUntil > k) return;
//Move m_PictureBox towards m_PointsToVisit.First
//when m_PictureBox.Location == m_PointToVisit.First, check if the InterestingPoint
//has a action you need to handle,
//like if the action is stop, you set m_StoppedUntil to k+ the number of frames you
//want the picturebox to stay still. (m_StoppedUntil = k+10 => doesnt update for 10 frames)
//if the action is checkdestroy, see if it shall be destroyed and set DestroyMe to true
}
So, you initialize your MovingImage objects with a picturebox and list of point it should move to, these points contain a action for the object to execute when it arrives there.
Keep your MovingImages in a list of some kind and iterate over them to update them.
In your Timer_Tick you do something like
void timer_Tick(object sender, EventArgs e)
{
k++
foreach(MovingImage m in m_MyMovingImages)
{
m.Update(k);
if(m.DestroyMe)
{
//Destroy it.
}
}
}

Categories