简体   繁体   中英

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

Did you try to put Thread.Sleep(100); right after sY = mY; 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)
   }

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM