介绍:
我建立了一个类似于抛物线运动模拟器的东西,给定初始速度和角度,在每次“Fire”按钮被点击时,就会生成一个新的子弹,并沿着计算路径进行动画展示。路径只计算一次并用于所有子弹。
问题:
我遇到的问题是在应用程序性能出现问题之前可以正常进行动画处理的项目数量(例如30个)似乎相对较低,并且这取决于初始速度或角度。当出现问题时,“Fire”按钮的响应速度明显变慢,而子弹似乎排队并以突发方式发射,而不是在按钮单击时发射。
解决该问题的尝试:
- 最初编写时,每次点击按钮都会为每个子弹计算路径,因此我首先认为这可能是问题所在。我将其重写为如果初始值相同,则重复使用相同的路径。但它没有解决问题。
- 然后我想也许我可以将一个子弹的动画责任交给一个单独的线程。在我的研究中,我了解到处理线程亲和力的困难,但仍然尝试了一下。我为每个新线程提供了一个要执行的方法,其中包含以下内容:
this.Dispatcher.Invoke((Action)(() => { /*Code for creating and starting animation*/ }));
它可以很好地创建和动画化一个子弹,但并没有解决或甚至改善主要问题。也许这不是正确的方法。 - 最近,我尝试降低帧率,这有所帮助,但只是稍微有所改善。
问题:
还有哪些选项,或者是否应该使用其他线程方法,还是我正在使用的矩阵动画的本质,应该考虑使用另一种类型的动画?
我注意到可能存在的相关性是,初始速度或需要覆盖的水平距离越大,平稳动画的子弹数量就会减少,反之亦然(例如,速度较慢或需要覆盖的水平距离较小将增加数量)。
我在笔记本电脑和高端台式机上运行了应用程序,并获得了类似的结果。
以下是负责创建新子弹并进行动画处理而不使用多线程的主要代码部分。我本来想包括屏幕截图以帮助解释,但目前无法这样做。
提前感谢您的所有贡献!
这是一个Drop Box链接,包含一个压缩的演示项目和屏幕截图:Projectile Motion Demo
public partial class MainWindow : Window
{
Projectile bullet;
ProjectilePathMovement projMove;
private void spawnAndFireBullet()
{
// Create new bullet with: Velocity, Initial Angle, Damage
bullet = new Projectile(100, 45, 0);
bullet.Template = Resources["BulletTemplate"] as ControlTemplate;
canvas.Children.Add(bullet);
// Position the bullet at it's starting location.
Canvas.SetLeft(bullet, 50);
Canvas.SetTop(bullet, canvas.ActualHeight - 10);
projMove.animateProjectile(bullet, mainWindow);
}
}
public class ProjectilePathMovement
{
Storyboard pathAnimationStoryboard;
MatrixAnimationUsingPath projectileAnimation;
MatrixTransform projectileMatrixTransform;
ProjectileMotion pMotion = new ProjectileMotion();
public void animateProjectile(Projectile projectile, Window window)
{
NameScope.SetNameScope(window, new NameScope());
projectileMatrixTransform = new MatrixTransform();
projectile.RenderTransform = projectileMatrixTransform;
window.RegisterName("ProjectileTransform", projectileMatrixTransform);
projectileAnimation = new MatrixAnimationUsingPath();
projectileAnimation.PathGeometry = pMotion.getProjectilePath(projectile); // Get the path of the projectile.
projectileAnimation.Duration = TimeSpan.FromSeconds(pMotion.flightTime);
projectileAnimation.DoesRotateWithTangent = true;
Storyboard.SetTargetName(projectileAnimation, "ProjectileTransform");
Storyboard.SetTargetProperty(projectileAnimation, new PropertyPath(MatrixTransform.MatrixProperty));
pathAnimationStoryboard = new Storyboard();
pathAnimationStoryboard.Children.Add(projectileAnimation);
pathAnimationStoryboard.Begin(window);
}
}
class ProjectileMotion
{
// Trajectory variables.
public double trajRange = 0.0, trajHeight = 0.0, trajTime = 0.0;
private double gravity = 9.81; // m/s^2
private double velocity = 0.0; // m/s
private double angle = 0.0; // In radians
private double cosine, secant, tangent;
private double deltaX, deltaY;
private double x_component, y_component;
private double t_maxHeight;
private double start_x, start_y, current_x, current_y;
private double previousAngle = 0.0, previousVelocity = 0.0;
private PathGeometry projectilePath, previousProjectilePath; // The actual path of the object/projectile.
private PathFigure pFigure; // projectilePath is comprised of pFigure.
private PolyLineSegment polyLine; // polyLine is comprised of points.
private PointCollection points; // points is comprised of a list of all points in the path
/// <summary>
/// Returns the path the projectile would take given its initial velocity, initial angle, and starting point.
/// Pass the angle in Degrees.
/// </summary>
/// <param name="projectile"></param>
/// <param name="vel"></param>
/// <param name="ang"></param>
/// <param name="startPoint"></param>
/// <returns></returns>
public PathGeometry getProjectilePath(UIElement projectile, double vel, double ang, System.Windows.Point startPoint)
{
// Calculate the necessary values.
calculateValues(projectile, ang, vel);
// Derive the object's/projectile's path.
return deriveProjectilePath(startPoint);
}
public double getGravity()
{
return gravity;
}
private void calculateValues(UIElement projectile, double ang, double vel)
{
// Convert the angle from Degrees to Radians.
angle = ang * (Math.PI / 180);
velocity = vel;
cosine = Math.Cos(angle);
secant = 1 / cosine;
tangent = Math.Tan(angle);
deltaX = Math.Cos(angle);
deltaY = Math.Sin(angle);
// Get current coordinates.
start_x = Canvas.GetLeft(projectile);
start_y = Canvas.GetTop(projectile);
current_y = start_y;
current_x = start_x;
// Calculate the horizontal and vertical components of initial velocity.
// Xvo = Vo * Cos(angle)
// Yvo = Vo * Sin(angle)
x_component = velocity * Math.Cos(angle);
y_component = velocity * Math.Sin(angle);
// Calculate time to reach max height. t max = Vyo / 9.8
t_maxHeight = y_component / gravity;
// Calculate max height of projectile. h = Yo + Vyo·t - 0.5·g·t^2
trajHeight = 0 + (y_component * t_maxHeight) - (.5 * gravity * t_maxHeight * t_maxHeight);
//Calulate max range of projectile.
trajRange = (2 * (velocity * velocity) * Math.Sin(angle) * Math.Cos(angle)) / gravity;
// Calculate flight time.
trajTime = 2 * t_maxHeight;
}
private PathGeometry deriveProjectilePath(System.Windows.Point pt)
{
projectilePath = new PathGeometry();
pFigure = new PathFigure();
start_x = pt.X;
start_y = pt.Y;
current_y = start_y;
pFigure.StartPoint = pt;
polyLine = new PolyLineSegment();
points = new PointCollection();
// Checks if the angle and velocity for this projectile is the same as last time. If it is the same there is no need to recalculate the path of the projectile, just use the same one from before.
if (previousAngle != angle && previousVelocity != velocity)
{
// Loop to obtain every point in the trajectory's path.
for (current_x = start_x; current_x <= trajRange; current_x++)
{
current_y = start_y - current_x * tangent + ((gravity * current_x * current_x) / (2 * (velocity * velocity * cosine * cosine))); // Y = Yo + X*tan - ( (g*X^2) / 2(v*cos)^2 ) Trajectory Formula to find the 'y' value at a given 'x' value.
points.Add(new System.Windows.Point(current_x, current_y));
}
// If the last x-coordinate value exceeds the actual range of projectile set x = to actual range to
// obtain actual y-coordinate value for final trajectory point.
if (current_x > trajRange)
{
current_x = trajRange;
current_y = start_y - current_x * tangent + ((gravity * current_x * current_x) / (2 * (velocity * velocity * cosine * cosine))); // Y = Yo + X*tan - ( (g*X^2) / 2(v*cos)^2 ) Trajectory Formula to find the 'y' coord given an 'x' value.
points.Add(new System.Windows.Point(current_x, current_y));
}
polyLine.Points = points;
pFigure.Segments.Add(polyLine);
projectilePath.Figures.Add(pFigure);
}
else
{
projectilePath = previousProjectilePath;
}
// Freeze the PathGeometry for performance benefits?
projectilePath.Freeze();
previousVelocity = velocity;
previousAngle = angle;
previousProjectilePath = projectilePath;
return projectilePath;
}
}