C#-WinForms ||用于旋转的图形的多个实例
C#-WinForms || Multiple instances of Graphics for rotation
我目前正在从事一个学校项目,该项目基本上是一个涉及 GDI-Winforms 动画的游戏。
我选择了我的游戏,就像越狱一样,你试图逃离监狱,而拿着手电筒的守卫在房间里走来走去。
所以,我有代表守卫的 Guard class 并且有 A Path 可以走。此外,防护罩可以 90 度旋转。角度。 (动画到一定程度)
当我旋转守卫时,我实际上旋转了通过 Form_Paint 事件传递的 Graphics 对象:
void Game_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
ply.Draw(e.Graphics); // Draws the player
grd.Draw(e.Graphics); // Draws the guard
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
}
当我只对付一名后卫时,效果很好。顺利,并做了他应该做的事情。
当我试图再增加 1 名警卫时,他们开始惊慌失措。很快我发现这是因为我派两个守卫 去绘制同一个 Graphics 实例。这也意味着,他们都在旋转它。
假设一个旋转-90,另一个旋转,但是他的 angle class 成员变量不会是-90,那么一切都会崩溃。
图形旋转方法(坐在守卫里面class):
public Graphics RotateGuard(Graphics g, Point pivot, float angle) // returns the rotated Graphics object
{
if (!float.IsNaN(angle))
{
using (Matrix m = new Matrix())
{
m.RotateAt(angle, pivot);
g.Transform = m;
}
}
return g;
}
接下来我做的是给他们每个人 this.CreateGraphics()
。
void Game_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
ply.Draw(e.Graphics); // Draws the player
foreach (Guard grd in this.guards )
{
grd.Draw(this.CreateGraphics()); // Draws the guard
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
}
}
然后一切正常。唯一的问题是,GPU 处理起来似乎真的很繁重。它比他应该绘制的每 5 帧少绘制一次守卫。
我用谷歌搜索,但除了人们所说的 "There is no need to clone a Graphics object" 之外找不到任何东西,但我仍然想不出任何更好的工作解决方案。
如何才能很好地解决这个问题?
提前致谢。
使用旋转的 Graphics
工具对象绘制后,只需调用 e.Graphics.ResetTransform()
。
如果您进行了一些设置,您可能还想查看 Graphics.Save()
和 Graphics.Restore()
以 return 到..它可以保存几个状态,完成后他们把他们带回来。非常好,至少如果你记下自己在做什么。
当然,您 可以 通过以相反的顺序执行反向调用来撤消 Translation/Rotation,但其他方法更简单,只适合您的情况.
请注意 Graphics
不 包含 任何图形,它是一个 工具,用于绘制 到关联的 Bitmap
或在控件的表面上..
最后:永远,永远不要使用CreateGraphics
!!!它的结果是非持久性,你很少需要它..
一种有效的方法是维护一个整体变换,并为每个要绘制的对象创建一个矩阵。在绘制之前,您将当前变换与对象的变换相乘。之后您在下一步绘制之前重置变换。
void Game_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var g = e.Graphics;
g.ResetTransform();
g.MultiplyTransform (playerMatrix);
ply.Draw(e.Graphics); // Draws the player
g.ResetTransform();
foreach (Guard grd in this.guards )
{
g.MultiplyTransform (grd.Matrix);
grd.Draw(this.CreateGraphics()); // Draws the guard
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
g.ResetTransform();
}
}
这些概念类似于 Direct3D 等 3D 图形中的处理方式;新华社; OpenGL 和 Unity3D。
CreateGraphics()
真是个坏主意。您应该对 PaintEventArgs
传递的 Graphics
进行所有绘制。所以你的初始代码就好了。但是,您需要做的是确保每个在其 Draw
方法中接收到 Graphics
的对象在完成其工作后保持不变。这可以通过像这样使用 Graphics.Save and Graphics.Restore
来实现
class Guard
{
public void Draw(Graphics g)
{
var state = g.Save();
try
{
// The actual drawing code
}
finally
{
g.Restore(state);
}
}
}
我弄清楚了它是什么,然后我使用了@Ivan 所做的。
void Game_Paint(object sender, PaintEventArgs e)
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var saved = e.Graphics.Save();
ply.Draw(e.Graphics); // Draws the player
foreach (Guard grd in this.guards )
{
grd.Draw(e.Graphics); // Draws the guard
e.Graphics.Restore(saved);
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
}
}
我所要做的就是没有使用 this.DoubleBuffered
,而是使用了 SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
,起初我认为两者的作用相同,但显然不是。
然后,我保存了当前的图形状态并重新绘制。
非常感谢大家!
我目前正在从事一个学校项目,该项目基本上是一个涉及 GDI-Winforms 动画的游戏。 我选择了我的游戏,就像越狱一样,你试图逃离监狱,而拿着手电筒的守卫在房间里走来走去。
所以,我有代表守卫的 Guard class 并且有 A Path 可以走。此外,防护罩可以 90 度旋转。角度。 (动画到一定程度) 当我旋转守卫时,我实际上旋转了通过 Form_Paint 事件传递的 Graphics 对象:
void Game_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
ply.Draw(e.Graphics); // Draws the player
grd.Draw(e.Graphics); // Draws the guard
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
}
当我只对付一名后卫时,效果很好。顺利,并做了他应该做的事情。 当我试图再增加 1 名警卫时,他们开始惊慌失措。很快我发现这是因为我派两个守卫 去绘制同一个 Graphics 实例。这也意味着,他们都在旋转它。 假设一个旋转-90,另一个旋转,但是他的 angle class 成员变量不会是-90,那么一切都会崩溃。
图形旋转方法(坐在守卫里面class):
public Graphics RotateGuard(Graphics g, Point pivot, float angle) // returns the rotated Graphics object
{
if (!float.IsNaN(angle))
{
using (Matrix m = new Matrix())
{
m.RotateAt(angle, pivot);
g.Transform = m;
}
}
return g;
}
接下来我做的是给他们每个人 this.CreateGraphics()
。
void Game_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
ply.Draw(e.Graphics); // Draws the player
foreach (Guard grd in this.guards )
{
grd.Draw(this.CreateGraphics()); // Draws the guard
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
}
}
然后一切正常。唯一的问题是,GPU 处理起来似乎真的很繁重。它比他应该绘制的每 5 帧少绘制一次守卫。
我用谷歌搜索,但除了人们所说的 "There is no need to clone a Graphics object" 之外找不到任何东西,但我仍然想不出任何更好的工作解决方案。
如何才能很好地解决这个问题? 提前致谢。
使用旋转的 Graphics
工具对象绘制后,只需调用 e.Graphics.ResetTransform()
。
如果您进行了一些设置,您可能还想查看 Graphics.Save()
和 Graphics.Restore()
以 return 到..它可以保存几个状态,完成后他们把他们带回来。非常好,至少如果你记下自己在做什么。
当然,您 可以 通过以相反的顺序执行反向调用来撤消 Translation/Rotation,但其他方法更简单,只适合您的情况.
请注意 Graphics
不 包含 任何图形,它是一个 工具,用于绘制 到关联的 Bitmap
或在控件的表面上..
最后:永远,永远不要使用CreateGraphics
!!!它的结果是非持久性,你很少需要它..
一种有效的方法是维护一个整体变换,并为每个要绘制的对象创建一个矩阵。在绘制之前,您将当前变换与对象的变换相乘。之后您在下一步绘制之前重置变换。
void Game_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var g = e.Graphics;
g.ResetTransform();
g.MultiplyTransform (playerMatrix);
ply.Draw(e.Graphics); // Draws the player
g.ResetTransform();
foreach (Guard grd in this.guards )
{
g.MultiplyTransform (grd.Matrix);
grd.Draw(this.CreateGraphics()); // Draws the guard
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
g.ResetTransform();
}
}
这些概念类似于 Direct3D 等 3D 图形中的处理方式;新华社; OpenGL 和 Unity3D。
CreateGraphics()
真是个坏主意。您应该对 PaintEventArgs
传递的 Graphics
进行所有绘制。所以你的初始代码就好了。但是,您需要做的是确保每个在其 Draw
方法中接收到 Graphics
的对象在完成其工作后保持不变。这可以通过像这样使用 Graphics.Save and Graphics.Restore
class Guard
{
public void Draw(Graphics g)
{
var state = g.Save();
try
{
// The actual drawing code
}
finally
{
g.Restore(state);
}
}
}
我弄清楚了它是什么,然后我使用了@Ivan 所做的。
void Game_Paint(object sender, PaintEventArgs e)
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var saved = e.Graphics.Save();
ply.Draw(e.Graphics); // Draws the player
foreach (Guard grd in this.guards )
{
grd.Draw(e.Graphics); // Draws the guard
e.Graphics.Restore(saved);
e.Graphics.DrawPolygon(Pens.Red, grd.GetPath()); // Draws the guard's path
}
}
我所要做的就是没有使用 this.DoubleBuffered
,而是使用了 SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
,起初我认为两者的作用相同,但显然不是。
然后,我保存了当前的图形状态并重新绘制。
非常感谢大家!