MonoGame 绘制函数不重绘
MonoGame draw function is not redrawing
我最近开始玩 monogame 并了解它是如何工作的,我想制作一个排序算法的可视化。
当我启动程序时,draw() 函数不会以当前状态重绘列。
它显示第一个状态,在循环结束时跳转到排序状态。
我是不是漏掉了什么?
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
if (Keyboard.GetState().IsKeyDown(Keys.Enter))
{
int temp;
for (int j = 0; j <= MainArray.Length - 2; j++)
{
for (int i = 0; i <= MainArray.Length - 2; i++)
{
if (MainArray[i] > MainArray[i + 1])
{
temp = MainArray[i + 1];
MainArray[i + 1] = MainArray[i];
MainArray[i] = temp;
Draw(gameTime);
Thread.Sleep(300);
}
}
}
}
// TODO: Add your update logic here
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
for (int i = 0; i < MainArray.Length; i++)
{
sprite.Draw(new Vector2(i * 40 + 20, -10), spriteBatch, new Rectangle(i * 40 + 30, 0, 30, MainArray[i] * 8));
}
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
调用 Draw()
只会将您要求它绘制的内容添加到一种 “绘制缓冲区” 中,该缓冲区存储应绘制的内容。它不是直接显示屏幕上的像素。
在屏幕上显示像素的操作是由Monogame在调用Update()
后在内部完成的。在您的代码中,您正在用新的排序状态覆盖 "绘图缓冲区"。当需要在屏幕上显示像素时,Monogame 会获取 “绘图缓冲区” 上的任何内容并进行渲染。这就是为什么你只能看到最后的状态。
Monogame 游戏的内部循环大致如下所示:
public void Tick()
{
(...)
DoUpdate() // Calls "Update()"
(...)
DoDraw() // Calls "Draw()"
// Nested in some other methods, it also calls "Platform.Present()",
// which is the place where pixels are displayed on the screen
(...)
}
这是我从the source code of Monogame's Game.cs
file中了解到的。
解决此问题的一种方法是 运行 另一个线程中的排序算法,并在需要时更新要显示的值,如下所示:
protected override void Initialize()
{
latestArray = MainArray;
sortingThread = new Thread(new ThreadStart(Sorting));
sortingThread.IsBackground = true;
sortingThread.Start();
}
private void Sorting()
{
float temp;
for (int j = 0; j <= MainArray.Length - 2; j++) {
for (int i = 0; i <= MainArray.Length - 2; i++) {
if (MainArray[i] > MainArray[i + 1]) {
temp = MainArray[i + 1];
MainArray[i + 1] = MainArray[i];
MainArray[i] = temp;
latestArray = MainArray;
Thread.Sleep(10);
}
}
}
Console.WriteLine("Sorting done !");
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
displayedArray = latestArray;
for (int i = 0; i < displayedArray.Length; i++) {
// Draw column
}
spriteBatch.End();
}
您的排序很好。
对于 MonoGame 和一般的游戏编程,您必须记住 Update() 和 Draw() 已经循环,并自动调用。
您应该永远不要在游戏 1 中自行调用 Update() 或 Draw()。由于 Draw() 实际上并未在屏幕上呈现任何内容,因此您正在浪费 CPU 个周期。
与其他编程范式不同,游戏循环(更新和绘制)无所不包,什么都不等待。虽然可以打破这条规则,但不应该在大多数情况下。
要将时间方面应用到传统循环,必须跨调用 Update() 来完成。
当应用于嵌套循环时,循环的顺序会反转,因为内部循环比外部循环运行“更快”,或者说更常见。
对循环实施了延迟机制(计时器),以减慢帧从默认的 60 fps(16.66667 毫秒)到合理且可见的(~2.9 fps)每帧 300 毫秒的变化。
你的代码有一个错误:冒泡排序在最坏的情况下需要 n * n 次迭代(从大到小排序),你的代码 运行s: n(n-1) 次, loop for j 在达到阈值之前终止一次迭代。我已经解决了这个问题。
以下代码使用这些输入约定:
- 在开始之前等待按下“Enter”。
- 随后的“输入”将 pause/resume 进度。
- 在最后停止,在下一个“Enter”重新开始
此代码还提供了提前退出条件,要求每次通过都进行更改(交换),否则退出。此代码使冒泡排序复杂度更接近 n log n
.
额外需要 Game1 class 关卡变量,在以下行添加:
public class Game1:Game
{
请注意,以下变量名称可能不符合某些命名准则。请相应调整。
private int j = 0, i = 0; // move outside since they must carry across calls of Update()
private bool isRunning = false; //Indicate status, stop when paused or done.
private double timer = 0; //accumulator for time in ms
private int timeBetweenFrames = 300; //in ms, when to run next loop iteration
private KeyboardState ks = new KeyboardState(), oks; // needed for key press code
更新()
protected override void Update(GameTime gameTime)
{
oks = ks;
ks = Keyboard.GetState();
if(ks.IsKeyDown(Keys.Escape) Exit();
// new exit code, better due to the single Getstate() call per step/tick/iteration.
if (ks.IsKeyDown(Keys.Enter) && oks.IsKeyUp(Keys.Enter)) // wait for an Enter press
//must be released then pressed before it fires again
// without the additional check this will fire 60 times per second
{
timer = 0;
// i = 0; j = 0; //for reset instead of pause on enter
isRunning = !isRunning;
}
if (isRunning)
{
timer += gameTime.ElapsedGameTime.TotalMilliseconds;
// should be 16.6(1/60) for the default 60 fps
if (timer >= timeBetweenFrames) //wait for timer
{
timer=0; // reset timer and do a loop iteration
//inner loop first, outer is incremented/checked in the else
int temp=-1; // -1 to allow early exit
if(++i <= MainArray.Length - 2)
{
// loop body
if (MainArray[i] > MainArray[i + 1])
{
temp = MainArray[i + 1];
MainArray[i + 1] = MainArray[i];
MainArray[i] = temp;
}
}
else // outer increment and check
{
if(++j >= MainArray.Length || (i < MainArray.Length - 1 && temp == -1))
// corrected bug and provide early exit
// bug in worst case, reverse order source, needs n*n runs
{
//Done
isRunning = false; // stop running
//reset for next run
i = 0;
j = 0;
timer=0;
}
}
}
}
base.Update(gameTime);
}
我最近开始玩 monogame 并了解它是如何工作的,我想制作一个排序算法的可视化。
当我启动程序时,draw() 函数不会以当前状态重绘列。
它显示第一个状态,在循环结束时跳转到排序状态。
我是不是漏掉了什么?
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
if (Keyboard.GetState().IsKeyDown(Keys.Enter))
{
int temp;
for (int j = 0; j <= MainArray.Length - 2; j++)
{
for (int i = 0; i <= MainArray.Length - 2; i++)
{
if (MainArray[i] > MainArray[i + 1])
{
temp = MainArray[i + 1];
MainArray[i + 1] = MainArray[i];
MainArray[i] = temp;
Draw(gameTime);
Thread.Sleep(300);
}
}
}
}
// TODO: Add your update logic here
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
for (int i = 0; i < MainArray.Length; i++)
{
sprite.Draw(new Vector2(i * 40 + 20, -10), spriteBatch, new Rectangle(i * 40 + 30, 0, 30, MainArray[i] * 8));
}
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
调用 Draw()
只会将您要求它绘制的内容添加到一种 “绘制缓冲区” 中,该缓冲区存储应绘制的内容。它不是直接显示屏幕上的像素。
在屏幕上显示像素的操作是由Monogame在调用Update()
后在内部完成的。在您的代码中,您正在用新的排序状态覆盖 "绘图缓冲区"。当需要在屏幕上显示像素时,Monogame 会获取 “绘图缓冲区” 上的任何内容并进行渲染。这就是为什么你只能看到最后的状态。
Monogame 游戏的内部循环大致如下所示:
public void Tick()
{
(...)
DoUpdate() // Calls "Update()"
(...)
DoDraw() // Calls "Draw()"
// Nested in some other methods, it also calls "Platform.Present()",
// which is the place where pixels are displayed on the screen
(...)
}
这是我从the source code of Monogame's Game.cs
file中了解到的。
解决此问题的一种方法是 运行 另一个线程中的排序算法,并在需要时更新要显示的值,如下所示:
protected override void Initialize()
{
latestArray = MainArray;
sortingThread = new Thread(new ThreadStart(Sorting));
sortingThread.IsBackground = true;
sortingThread.Start();
}
private void Sorting()
{
float temp;
for (int j = 0; j <= MainArray.Length - 2; j++) {
for (int i = 0; i <= MainArray.Length - 2; i++) {
if (MainArray[i] > MainArray[i + 1]) {
temp = MainArray[i + 1];
MainArray[i + 1] = MainArray[i];
MainArray[i] = temp;
latestArray = MainArray;
Thread.Sleep(10);
}
}
}
Console.WriteLine("Sorting done !");
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
displayedArray = latestArray;
for (int i = 0; i < displayedArray.Length; i++) {
// Draw column
}
spriteBatch.End();
}
您的排序很好。
对于 MonoGame 和一般的游戏编程,您必须记住 Update() 和 Draw() 已经循环,并自动调用。
您应该永远不要在游戏 1 中自行调用 Update() 或 Draw()。由于 Draw() 实际上并未在屏幕上呈现任何内容,因此您正在浪费 CPU 个周期。
与其他编程范式不同,游戏循环(更新和绘制)无所不包,什么都不等待。虽然可以打破这条规则,但不应该在大多数情况下。
要将时间方面应用到传统循环,必须跨调用 Update() 来完成。
当应用于嵌套循环时,循环的顺序会反转,因为内部循环比外部循环运行“更快”,或者说更常见。
对循环实施了延迟机制(计时器),以减慢帧从默认的 60 fps(16.66667 毫秒)到合理且可见的(~2.9 fps)每帧 300 毫秒的变化。
你的代码有一个错误:冒泡排序在最坏的情况下需要 n * n 次迭代(从大到小排序),你的代码 运行s: n(n-1) 次, loop for j 在达到阈值之前终止一次迭代。我已经解决了这个问题。
以下代码使用这些输入约定:
- 在开始之前等待按下“Enter”。
- 随后的“输入”将 pause/resume 进度。
- 在最后停止,在下一个“Enter”重新开始
此代码还提供了提前退出条件,要求每次通过都进行更改(交换),否则退出。此代码使冒泡排序复杂度更接近 n log n
.
额外需要 Game1 class 关卡变量,在以下行添加:
public class Game1:Game
{
请注意,以下变量名称可能不符合某些命名准则。请相应调整。
private int j = 0, i = 0; // move outside since they must carry across calls of Update()
private bool isRunning = false; //Indicate status, stop when paused or done.
private double timer = 0; //accumulator for time in ms
private int timeBetweenFrames = 300; //in ms, when to run next loop iteration
private KeyboardState ks = new KeyboardState(), oks; // needed for key press code
更新()
protected override void Update(GameTime gameTime)
{
oks = ks;
ks = Keyboard.GetState();
if(ks.IsKeyDown(Keys.Escape) Exit();
// new exit code, better due to the single Getstate() call per step/tick/iteration.
if (ks.IsKeyDown(Keys.Enter) && oks.IsKeyUp(Keys.Enter)) // wait for an Enter press
//must be released then pressed before it fires again
// without the additional check this will fire 60 times per second
{
timer = 0;
// i = 0; j = 0; //for reset instead of pause on enter
isRunning = !isRunning;
}
if (isRunning)
{
timer += gameTime.ElapsedGameTime.TotalMilliseconds;
// should be 16.6(1/60) for the default 60 fps
if (timer >= timeBetweenFrames) //wait for timer
{
timer=0; // reset timer and do a loop iteration
//inner loop first, outer is incremented/checked in the else
int temp=-1; // -1 to allow early exit
if(++i <= MainArray.Length - 2)
{
// loop body
if (MainArray[i] > MainArray[i + 1])
{
temp = MainArray[i + 1];
MainArray[i + 1] = MainArray[i];
MainArray[i] = temp;
}
}
else // outer increment and check
{
if(++j >= MainArray.Length || (i < MainArray.Length - 1 && temp == -1))
// corrected bug and provide early exit
// bug in worst case, reverse order source, needs n*n runs
{
//Done
isRunning = false; // stop running
//reset for next run
i = 0;
j = 0;
timer=0;
}
}
}
}
base.Update(gameTime);
}