在 Windows 窗体中实现 gameloop [关闭]

Posted

技术标签:

【中文标题】在 Windows 窗体中实现 gameloop [关闭]【英文标题】:Implementing gameloop in Windows Forms [closed] 【发布时间】:2013-10-21 17:20:15 【问题描述】:

对于这个游戏的常见问题,我很抱歉,但我不知道在哪里以及如何在我的游戏中实现游戏循环。我希望游戏循环能够像这样工作:

Settings();
if (startClicked == true)

    Spawn();

while (enemyKilled == true)

    Spawn();

这是我的代码:

public partial class Form1 : Form

    public int score1 = 0;
    public PictureBox enemy = new PictureBox();
    public PictureBox missile = new PictureBox();
    public int enemyX;
    public int enemyY;
    public int missileX;
    public int missileY;
    public bool enemyKilled;
    public bool startClicked;
    public Form1()
    
        InitializeComponent();
        logo.Visible = false;
        startbutton.Visible = false;
        spaceship.Visible = false;
        score.Visible = false;
        labelEnemyX.Visible = false;
        labelEnemyY.Visible = false;
        labelMissileX.Visible = false;
        labelMissileY.Visible = false;

    

    private void Settings()
    
        logo.Visible = true;
        startbutton.Visible = true;
        score.Text = Convert.ToString(score1);
    

    private void Spawn()
    
        enemy.Visible = true;
        missile.Visible = true;
        System.Timers.Timer enemyMove = new System.Timers.Timer();
        enemyMove.Interval = 100;
        Random enemyPosition = new Random();
        int enemyX = enemyPosition.Next(30, 400);
        int enemyY = enemy.Location.Y;
        enemy.Location = new Point(enemyX, 0);
        enemy.Image = WindowsFormsApplication16.Properties.Resources.Enemy2;
        enemy.Width = 36;
        enemy.Height = 29;
        this.Controls.Add(enemy);
        enemyMove.Elapsed += (sender, args) =>
            
                enemy.Location = new Point(enemyX, enemy.Location.Y + 2);
                enemyX = enemy.Location.X;
                enemyY = enemy.Location.Y;
                labelEnemyX.Text = Convert.ToString(enemyX);
                labelEnemyY.Text = Convert.ToString(enemyY);
                if (enemyY <= -10)
                
                    enemyKilled = false;
                
            ; enemyMove.Start();
    

    private void AddScore()
    
        if (enemyKilled == true)
        
            score1++;
            score.Text = Convert.ToString(score1);
        
    

    private void MissileMove()
    
        System.Timers.Timer missileMove = new System.Timers.Timer();
        missileMove.Interval = 30;
        missileMove.Elapsed += (sender, args) =>
            
                if (missile.Location.Y >= -30)
                
                    missile.Location = new Point(missile.Location.X, missile.Location.Y - 15);
                    missileX = missile.Location.X;
                    missileY = missile.Location.Y;
                    labelMissileX.Text = Convert.ToString(missileX);
                    labelMissileY.Text = Convert.ToString(missileY);
                    int enemyToMissileLocationX = Convert.ToInt32(labelEnemyX.Text);
                    int enemyToMissileLocationY = Convert.ToInt32(labelEnemyY.Text);
                    if ((missileX - enemyToMissileLocationX <= 20 && missileY - enemyToMissileLocationY <= 20 && missileX - enemyToMissileLocationX >= 0 && missileY - enemyToMissileLocationY >= 0) || (enemyToMissileLocationX - missileX <= 20 && enemyToMissileLocationY - missileY <= 20 && enemyToMissileLocationX - missileX >= 0 && enemyToMissileLocationY - missileY >= 0))
                    
                        missile.Visible = false;
                        enemy.Visible = false;
                        enemyKilled = true;
                    
                
            ; missileMove.Start();

    

    void Form1_Load(object sender, EventArgs e)
    
        Settings();
        if (startClicked == true)
        
            Spawn();
        
        while (enemyKilled == true)
        
            Spawn();
        
    

    private void startbutton_Click(object sender, EventArgs e)
    
        this.startClicked = true;
        logo.Visible = false;
        startbutton.Visible = false;
        spaceship.Visible = true;
        score.Visible = true;
        startbutton.Enabled = false;

    

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    
        if (e.KeyCode == Keys.Right)
        
            if (spaceship.Location.X <= 370)
            
                spaceship.Location = new Point(spaceship.Location.X + 10, spaceship.Location.Y);
            
        
        if (e.KeyCode == Keys.Left)
        
            if (spaceship.Location.X >= 0)
            
                spaceship.Location = new Point(spaceship.Location.X - 10, spaceship.Location.Y);
            
        
        if (e.KeyCode == Keys.Space)
        
            missile.Image = WindowsFormsApplication16.Properties.Resources.Missile;
            missile.Height = 42;
            missile.Width = 12;
            missile.Location = new Point(spaceship.Location.X + 28, spaceship.Location.Y + 15);
            this.Controls.Add(missile);
            MissileMove();

        
    

更新:

    private void MissileMove()
    
        System.Timers.Timer missileMove = new System.Timers.Timer();
        missileMove.Interval = 30;
        missileMove.Elapsed += (sender, args) =>
            
                if (missile.Location.Y >= -30)
                
                    missile.Location = new Point(missile.Location.X, missile.Location.Y - 15);
                    missileX = missile.Location.X;
                    missileY = missile.Location.Y;
                    labelMissileX.Text = Convert.ToString(missileX);
                    labelMissileY.Text = Convert.ToString(missileY);
                    int enemyToMissileLocationX = Convert.ToInt32(labelEnemyX.Text);
                    int enemyToMissileLocationY = Convert.ToInt32(labelEnemyY.Text);
                    if ((missileX - enemyToMissileLocationX <= 20 && missileY - enemyToMissileLocationY <= 20 && missileX - enemyToMissileLocationX >= 0 && missileY - enemyToMissileLocationY >= 0) || (enemyToMissileLocationX - missileX <= 20 && enemyToMissileLocationY - missileY <= 20 && enemyToMissileLocationX - missileX >= 0 && enemyToMissileLocationY - missileY >= 0))
                    
                        missile.Visible = false;
                        enemy.Visible = false;
                        enemyKilled = true;
                        System.Threading.Thread.Sleep(100);
                        Application.DoEvents();
                        Spawn();
                    
                
            ; missileMove.Start();

尝试对班级进行此操作,是的,它再次生成了敌人,但动画出现了错误,我可以看到敌人从一个位置跳到另一个位置。 谢谢你的帮助。

【问题讨论】:

请添加更多详细信息 我希望游戏每次杀死敌人时都会生成敌人。 这是一个巨大的长代码,没有人会像思考一样阅读它。请更新您的问题,以获得更多答案。清理不必要的代码或方法体 我添加了整个代码,但问题是在哪里实现第一部分,即游戏循环,在 Windows 窗体中。 游戏循环需要一种截然不同的方式来编写代码。你必须扔掉所有东西,从头开始。没关系,0.2 版是相当不可避免的。 【参考方案1】:

我的建议是像 Form_Load 这样的函数(或者 main 函数,如果你熟悉将它集成到 Windows 窗体应用程序)中实现游戏循环,但添加对 Application.DoEvents 的调用在循环内部,以确保窗口仍然响应移动和调整大小以及单击按钮等事件。除此之外,您可能需要澄清当前解决方案遇到的问题,以获得更完整、更详细的答案。

【讨论】:

【参考方案2】:

这看起来像一个 Windows 窗体。因此,我建议您有一个在 while 循环中运行并处理游戏的所有移动/更新逻辑的后台线程。复杂的部分将是管理 Windows 窗体线程和游戏线程之间的状态。您需要确保您是用于更新游戏状态的按钮单击等事件处理程序以线程安全的方式执行此操作,因为它们将位于 GUI 线程上。游戏循环线程需要更新 GUI 上的状态,这将需要所谓的 marshelling,因为只有 GUI 线程才被允许访问表单上存在的控件,例如按钮等。

线程管理、线程安全和编组的主题留给你研究。

这假设它是某种实时游戏,而不是回合制游戏。如果它是回合制的,那么你真的不需要游戏循环。您可以通过使用状态图和纯事件驱动设计来实现相同的目标。

【讨论】:

不需要仅仅为了一个游戏循环就引入多线程的所有复杂性吗?只要循环包含Application.DoEvents() 并以合理的帧速率运行(因此 DoEvents 经常发生),一切都应该在单个线程上轻松运行。这就是由 Scrolling Game Development Kit 2 (sgdk2.sf.net) 创建的游戏的运作方式。 @BlueMonkMN 当然没有我建议的“需要”去做。但是,依赖 DoEvents 的架构有时被认为是不好的做法,因为它使 UI 的响应能力依赖于每次 DoEVents 调用之间的每一段代码,以 保证快速。话虽如此,我鼓励您将您的建议作为答案发布,因为我认为考虑到权衡,它同样有效。实现起来确实会简单得多。 我试图这样做,但问题是它会产生更多的敌人从一个敌人跳到另一个敌人。我用它添加了第三部分代码。

以上是关于在 Windows 窗体中实现 gameloop [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Delphi中实现MDI子窗体(转)

WinForm开发中实现控件随窗体大小的改变而自动适应其改变

怎样在C#中实现父窗体向子窗体传值和子窗体向父窗体传值

运行 CMD 和 ffmpeg Windows 窗体

不用注册热键方式在Delphi中实现定义快捷键(又简单又巧妙,但要当前窗体处在激活状态)

C#的两个控件在窗体移动。