第二位玩家移动输入滞后

时间:2017-06-30 23:44:22

标签: c# game-physics lag

问候!

我是C#的新手,我一直在关注YouTube的教程播放列表来创建相关应用程序。然而,现在我只是完成系列赛的一段视频,我决定尝试给我的游戏一点点我自己的扭曲。游戏非常基本,有一个 playerUnit (主要玩家),一个 enemyUnit (敌人)和 allyUnit (第二个玩家) 。我在教程系列的开头简单地创建了 allyUnit ,甚至没有想过将它变成第二个玩家。然而,凭借从其他剧集中获得的知识,我决定将其变成第二个玩家。在它自己的脚本中,我只使用相同的代码进行运动,我用于 playerUnit ,只更改运动键。但是,当按下键并且立即移动时, playerUnit 响应正确,而第二个玩家则滞后。它需要一段时间才能开始移动,有时会以不同的间隔移动。

Game.cs 脚本中,在第43和44行中,在 public void Run()中,我注意到如果我使用 allyUnit.Update 切换 playerUnit.Update allyUnit 将不再滞后,但 playerUnit 将会延迟。因此,代码中首先出现的不会滞后,另一个会出现。这让我觉得它可能与Update有些冲突?既然它同时更新键输入,在同一个循环中还是什么?鉴于我还在学习,我可能会说最大的亵渎,但这也是我创建这个主题的原因。

以下是相关脚本:

Game.cs

using System;
using System.Threading;
using System.Diagnostics;

namespace DodgeGame
{
    public class Game
    {
        public Game () //This function runs when the player starts a New Game, thus creating the characters
        {
            //Creates a player unit and sets its position/graphic
            playerUnit = new PlayerUnit(30, 10, "@");

            //Creates an enemy unit and sets its position/graphic
            enemyUnit = new EnemyUnit(Console.WindowWidth - 1, 10, "X");

            //Creates an ally unit and sets its position/graphic
            allyUnit = new AllyUnit(12, 5, "O");

            stopwatch = new Stopwatch();

        }

        private Stopwatch stopwatch;

        private Unit playerUnit;
        private Unit enemyUnit;
        private Unit allyUnit;

        public void Run() //This function is called once the New Game has begun, thus drawing the characters
        {
            stopwatch.Start ();
            int timeAtPreviousFrame = (int)stopwatch.ElapsedMilliseconds; //timeAtPreviousFrame -- milliseconds the stopwatch reported last frame


            while (true) 
            {
                //Time since last frame
                int deltaTimeMS = (int)(stopwatch.ElapsedMilliseconds - timeAtPreviousFrame); //1000 / desiredFPS;
                timeAtPreviousFrame = (int)stopwatch.ElapsedMilliseconds;

                //Update units
                playerUnit.Update (deltaTimeMS);
                allyUnit.Update (deltaTimeMS);
                enemyUnit.Update (deltaTimeMS);

                //Detect collide (True = Game Over)
                if (playerUnit.IsCollidingWith (enemyUnit) || allyUnit.IsCollidingWith(enemyUnit))
                {
                    GameOver ();    
                }

                //Draws all units
                playerUnit.Draw ();
                allyUnit.Draw ();
                enemyUnit.Draw ();

                //Tiny sleep to avoid running at too high FPS
                Thread.Sleep (5);   
            }
        }

        void GameOver()
        {
            Console.Clear ();
            Console.WriteLine ("Game Over!");
            Console.WriteLine ("\n\nWould you like to try again? (Y/N)");

            while(true)
            {
                ConsoleKeyInfo cki = Console.ReadKey (true);

                switch(cki.Key)
                {
                case ConsoleKey.Y:
                    Console.Clear ();
                    Game game = new Game ();
                    game.Run ();
                    break;

                case ConsoleKey.N:
                    Environment.Exit(0);
                    break;
                }
            }

        }
    }
}

AllyUnit.cs

using System;

namespace DodgeGame
{
    public class AllyUnit : Unit
    {
        public AllyUnit (int x, int y, string unitGraphic) : base(x, y, unitGraphic)    {       }

        override public void Update(int deltaTimeMS)
        {
            //Ally Actions

            //Has the second player pressed a key?
            if (Console.KeyAvailable == true)       // Yes
            { 
                //This will make the second player move
                ConsoleKeyInfo cki = Console.ReadKey (true);

                switch (cki.Key) 
                {
                    //Move Up
                    case ConsoleKey.UpArrow:    case ConsoleKey.NumPad8:
                        if (Y > 0)
                            Y = Y - 1;

                        break;

                    //Move Down
                    case ConsoleKey.DownArrow:  case ConsoleKey.NumPad2:
                        if (Y < Console.WindowHeight - 1)
                            Y = Y + 1;

                        break;

                    //Move Left
                    case ConsoleKey.LeftArrow:  case ConsoleKey.NumPad4:
                        if (X > 0)
                            X = X - 1;

                        break;

                    //Move Right
                    case ConsoleKey.RightArrow: case ConsoleKey.NumPad6:
                        if (X < Console.WindowWidth - 1)
                            X = X + 1;

                        break;
                }

                //With Ally Actions calculated, we want to call Unit's Update in case it has important work to do
                base.Update (deltaTimeMS);
            }
        }
    }
}

PlayerUnit.cs

using System;

namespace DodgeGame
{
    public class PlayerUnit : Unit
    {
        public PlayerUnit (int x, int y, string unitGraphic) : base(x, y, unitGraphic)
        {

        }

        override public void Update(int deltaTimeMS)
        {
            //====================================================================\\
            //                           PLAYER ACTIONS                           \\
            //====================================================================\\

            //Has the user pressed a key?
            if (Console.KeyAvailable == true) // Yes
            {
                //This will make the player move
                ConsoleKeyInfo cki = Console.ReadKey (true);

                switch(cki.Key)
                {
                    //Move Up
                    case ConsoleKey.W: case ConsoleKey.NumPad8:
                        if(Y > 0)
                            Y = Y - 1;

                        break;

                    //Move Down
                    case ConsoleKey.S: case ConsoleKey.NumPad2:
                        if(Y < Console.WindowHeight - 1)
                            Y = Y + 1;

                        break;

                    //Move Left
                    case ConsoleKey.A: case ConsoleKey.NumPad4:
                        if(X > 0)   
                            X = X - 1;

                        break;

                    //Move Right
                    case ConsoleKey.D: case ConsoleKey.NumPad6:
                        if(X < Console.WindowWidth - 1)
                            X = X + 1;

                        break;
                }
            }

            //With keyboard input received, we want to call Unit's Update in case it has important work to do
            base.Update (deltaTimeMS);
        }
    }
}

Unit.cs

using System;

namespace DodgeGame
{
    abstract public class Unit
    {
        public Unit(int x, int y, string unitGraphic)
        {
            this.X = x;
            this.Y = y;
            this.UnitGraphic = unitGraphic;
        }

        //Unit's Position
        public int X //The way the rest of the program interacts with X
        {
            get
            {
                return _x;
            }
            set
            {
                if(value < 0 || value >= Console.WindowWidth)
                {
                    throw new Exception ("Invalid X Coordinate."); 
                }

                Clean ();
                _x = value;
            }
        }
        private int _x; //Where the value of X is actually stored

        //Unit's Position
        public int Y //The way the rest of the program interacts with X
        {
            get
            {
                return _y;
            }
            set
            {
                if(value < 0 || value >= Console.WindowHeight)
                {
                    throw new Exception ("Invalid Y Coordinate."); 
                }

                Clean ();
                _y = value;
            }
        }
        private int _y; //Where the value of X is actually stored

        //====================================================================\\

        //Unit's Graphics
        public string UnitGraphic {get; set;}

        //Update Unit's Position
        virtual public void Update(int deltaTimeMS)
        {

        }

        //This draws the units on the screen
            public void Draw()
        {
            Console.SetCursorPosition (X, Y);
            Console.Write (UnitGraphic);
        }

        public void Clean()
        {
            Console.SetCursorPosition (X, Y);
            Console.Write(' ');
        }

        public bool IsCollidingWith(Unit other)
        {
            if (this.X == other.X && this.Y == other.Y)
            {
                return true;
            }
            return false;

        }    
    }
}

我尝试专门为 allyUnit 创建一个不同的单独的 Update()函数,但它不起作用。我还尝试了其他一些小事,但没有人做过这件事。我该怎么做才能解决这个问题?

提前谢谢你,

Nervly

1 个答案:

答案 0 :(得分:-1)

我怀疑您的PlayerUnit和AllyUnit Update()方法都调用了ReadKey()。当首先调用任何Update()方法时,将抓住按下的键,不再为第二个方法调用留下按键事件。

无论如何,将任何类型的I / O直接包含在逻辑类中都是不好的形式,所以我要做的是在Game.cs中创建一个单独的Update函数来读取密钥,然后将密钥读取传递给PlayerUnit。 Update()和AllyUnit.Update()调用。

编辑: 在Game.cs中有类似的东西:

Update(mSecs) {
  ConsoleKeyInfo cki = Console.ReadKey();
  playerUnit.Update(cki, mSecs);
  allyUnit.Update(cki, mSecs);
  enemyUnit.Update(mSecs);
}

然后相应地更改* Unit类中的方法。 您还必须更改Run()方法,以通过调用此方法来替换Update调用。