Page 1 of 1

ballistics.

Posted: Fri Aug 05, 2011 1:49 am
by sazon
hello well my intention is to create a game (not shooter) in which there will be many shots of projectiles: bullets, missiles, etc..

I want to know if there is any guidance or good practice that should be used with BEPU

Re: ballistics.

Posted: Fri Aug 05, 2011 3:00 am
by Norbo
To give specific tips, I would need more information, but here's a couple of general ones:
-Extremely fast, tiny projectiles should probably just be made into ray casts. It's cheaper than using an actual tiny entity with CCD enabled. If you absolutely need to have some 'physics' applied to the extremely fast objects, you can perform a raycast each frame from the current location to a position computed by you. If it hits something, the end; if it doesn't, move to the next position.

-Larger objects that still move quickly should probably make use of continuous collision detection. This can be enabled by setting the entity's PositionUpdateMode to Continuous.

-If your platform is the Xbox360, then be careful about too many fully simulated projectiles.

Re: ballistics.

Posted: Fri Aug 05, 2011 2:41 pm
by speciesUnknown
I did this just the other day. My projectiles are simulated using gravity and drag (relative to their direction of motion). I'm using an object pool with swap/decrement object removal semantics. I extended the default BEPU Space class so I could do accelerated raycasts on a large number of projectiles at once. It's "pretty fast" (I've not really measured it but it runs nicely on a 1.86ghz core 2 laptop) and it doesn't perform any allocations at runtime, provided you prealloate enough space for projectiles; if it does run out, it will re-allocate a bigger block of projectiles and continue.

This is how you update such a system, and remove any now-dead projectiles. Note the "swapremove" semantic - what this does is swap the projectile to be removed with the last "living" projectile in the list, and then decrement the projectile count. My particles are classes, so this equates to a couple of copies of a reference. My projectiles are linked with their owning entity and have to clear this link.

Code: Select all

		public override void update(float dt)
		{
			base.update(dt);

			Projectile[] ps = projectiles.Data;

			for (int i = 0; i < projectiles.Used; )
			{
				// if projectile has been terminated, or it has exceeded the "safe" projectile time allowance, kill it off
				if (ps[i].terminate == true || ps[i].survival_time <= ps[i].age)
				{
					ps[i].clear();
					projectiles.swapRemove(i);
				}
				else
				{
					ps[i].nextStep(dt);
					i++;
				}
			}

			this.physics_component.projectileRayCast(projectiles, this.Callbacks.ProjectileRayCast);
		}
This is the custom raycast method I wrote:

Code: Select all

	/// <summary>
	/// An extension of the BEPU physics class Space, with some game-specific methods which are much faster if it has access to the guts of BEPU
	/// </summary>
	public class CustomBepuSpace : Space
	{
		protected Ray r_Ray;
		
		protected List<RayCastResult> r_List_RayCastResult = new List<RayCastResult>(1024);
		public void projectileRayCast(ObjectPool<Projectile> projectiles, ProjectileRayCastCallback callback)
		{
			if (callback == null)
				throw new Exception("It is expected that the callback is not null");

			// exit early if presented with empty set of projectiles
			if(projectiles.Used == 0)
				goto safe_return;

			for (int i = 0; i < projectiles.Used; i++)
			{
				Projectile p = projectiles.Data[i];
				
				// TG_TODO: make this work along the lines of the projectile's current and last position
				// this means refactoring projectile somewhat
				r_Ray.Position = p.current_position;
				r_Ray.Direction = p.current_velocity * (1f / 60f);

				// get a list of broadphase raycast results
				this.RayCast(r_Ray, 1f, r_List_RayCastResult);

				foreach (var result in r_List_RayCastResult)
				{
					PhysicsObjectTag tag = null;
					if ((tag = result.HitObject.Tag as PhysicsObjectTag) != null)
					{
						if (callback != null)
						{
							if (!callback(tag, p))
								goto skip_projectile;
						}
					}
				}
				skip_projectile:
				r_List_RayCastResult.Clear();
			}

			safe_return:
			
			r_List_RayCastResult.Clear();
		}
Note that I am reusing a single raycast result, again, this is to avoid runtime allocations.


edit: removed inaccurate code comment