Delta Timing – Papervision3D Example

5 02 2009

I got several questions about Delta timing and as this is such a vital and exciting technique to use in EVERY aspect of Flashprogramming as soon as you want to withhold an exact speed of your flash no matter how bad or good the host computer is. Here comes a nice Papervision example.

First let’s create the document class so that is out of the way:

package {
	import flash.display.Sprite;

	public class DeltaTime extends Sprite
	{
		public function DeltaTime()
		{
			var tView:View3d = new View3d()
			this.addChild(tView)
		}
	}
}

and now to the real part:

package
{
	import flash.events.Event;
	import org.papervision3d.lights.PointLight3D;
	import org.papervision3d.materials.ColorMaterial;
	import org.papervision3d.materials.shadematerials.PhongMaterial;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.objects.primitives.Sphere;
	import org.papervision3d.view.BasicView;
	import org.papervision3d.view.layer.util.ViewportLayerSortMode;

	public class View3d extends BasicView
	{
		private var light:PointLight3D
		private	var sphere1:Sphere
		private var sphere2:Sphere
		private var ground:Plane
		private var speed:Number
		private var expTimeInMillisecs:Number
		private var currentTime:int
		private var lastTime:int
		private var DELTA_SCALE:Number

		public function View3d()
		{
			super();
			setupScene()
			startRendering()
			initDeltaTiming()
		}

		private function setupScene():void
		{
			// set up camera so we an see the whole scene from a good perspective
			camera.y = 1100
			camera.z = -700

			// setup ground
			ground = new Plane(new ColorMaterial(0x224422),1600,1000,1,1)
			ground.rotationX = 90

			// create a light for the PhongMaterial to do it's shiny tricks)
			light = new PointLight3D()
			light.x = 400
			light.z = -400
			light.y = 600

			// creating a red sphere - this one will be the normal sphere
			sphere1 = new Sphere(new PhongMaterial(light,0xff1111,0x333333,1))
			sphere1.x = -800
			sphere1.y = 50
			sphere1.z = 200

			//creating a blue sphere - this will use Delta Timing.
			sphere2 = new Sphere(new PhongMaterial(light,0x3333ff,0x333333,1))
			sphere2.x = -800
			sphere2.y = 50
			sphere2.z = -200

			// add all objects to their own layer so no Z-fighting will appear.
			this.viewport.containerSprite.sortMode = ViewportLayerSortMode.INDEX_SORT
			this.viewport.getChildLayer(ground,true).layerIndex = 1
			this.viewport.getChildLayer(sphere1,true).layerIndex = 2
			this.viewport.getChildLayer(sphere2,true).layerIndex = 3

			// add all to the scene
			scene.addChild(ground)
			scene.addChild(sphere1)
			scene.addChild(sphere2)
		}

		private function initDeltaTiming():void
		{
			// we will make the spheres travel with the speed of 15.
			speed = 15
			// we also would like it to do that when the fps is 20.
			// so if the fps drops lower, the speed needs to be altered
			// to keep up our expected speed over screen.
			var expectedFPS:Number = 20

			// as FPS is "Frames Per Second" it's easy to calculate how
			// many millisecs it should take for one frame.
			expTimeInMillisecs = 1000/expectedFPS

			// just reseting the lastTime so DeltaTime doesn't
			// freak out at start
			lastTime = new Date().getTime()

		}

		private function calculateDeltaTime()
		{
			// first get the new current time (NOW that is)
			currentTime = new Date().getTime()

			// check how long time it took since last time
			// we were here (in Millisecs)
			var timeElapsed:int = currentTime-lastTime

			// calculate out the new DELTA_SCALE for the round
			// based on expected time it should have taken
			DELTA_SCALE = timeElapsed/expTimeInMillisecs

			// set lastTime to currentTime to be able to compare
			// next round we are in here.
			lastTime = currentTime
		}

		private function moveSpheres()
		{
			// sphere1 is moved with "normal" movement
			sphere1.x += speed
			if (sphere1.x > 800)
			{
				sphere1.x = -800
			}

			//sphere2 is moved with delta timing
			sphere2.x += speed * DELTA_SCALE
			if (sphere2.x > 800)
			{
				sphere2.x = -800
			}

		}

		override protected function onRenderTick(event:Event = null):void
		{
			// .. normally a lot more happens here to slow a game down but not today...

			calculateDeltaTime()
			moveSpheres()

			trace("DELTA_SCALE:"+DELTA_SCALE)

			super.onRenderTick(event)
		}
	}
}

Now set this up in your preferred Flashenvironment and try to run the app in different FPS-settings. You will find that the red sphere will move faster or slower depending on the current FPS. But the blue one will constantly move over the ground in the same speed regardless of the current fps.

Why then does the spheres not move equally fast if we set the Flash to run at 20 FPS ?? Well, as many of you flashdevelopers already are aware about is that Flash cannot assure the framrate to be exactly 20, so even then, the blue sphere is moving more correct than the red one due to small differences in the framerate.

Hope you got it all explained in more detail now. Have a nice evening, I will.

Advertisements




Multiplayer in Flash – How to..

5 02 2009

The biggest reason I chose to start up this project at this current date is that before current date it has not been possible to create what I am trying to do , using Flash. In fact, it is not completely possible yet when it comes to some 3D features but I’m an optimist and would like to stay prepared for what both PV3D and Flash has to offer the years to come (yes, I count this project to be in progress for years). Still when it comes to fast paced multiplayer games where two users, sitting by each computer, plays against each other it has just become reality!

Adobe has just opened the (beta)doors to their new protocol (RTMFP) and very shortly it means that.. it can now be done.

If you’re interested right now to try it out wothout reading my oracle guidelines, just head over to Adobes dev. Centre and check out Stratus

Without getting too technical on the subject I want to introduce what the difference is at why we need it, because this article will be about how to think to even be able to create a mp-game using streaming datatransfers client-client.

Netcommunication for dummies

Yes, we have been able to connect 2 (or more) flashclients directly to each other and communicate but until now it has only been available using a TCP-IP like protocol. (if you don’t know what it is, never mind, it is just porn for netfreaks). TCP-IP is very good in the aspect that the same data being sent is actually winding up in the right order and shape that it was sent. So, it is secure and stable. Problem is that the if “recieving client” doesn’t properly get the data sent it will wait until it really really gets it. No other data can be transfered meanwhile and in worst case it can choke up the whole system. In the end TCP/IP assures that the exact data being sent is coming in in the correct order. Maybe great for turnbased games and similar but not for our hot actiongame.

So now, something different has come up.¬† Adobe’s RTMFP is a UDP-based protocol (porn again) and UDP does not really care if all the data comes in in the exact order. It just keeps on sending and recieving data. This means that no “doublechecks” needs to be done clogging up the system and no chokes happenes if one package gets lost along the way. Don’t you worry, there will always come another package ;)

So basically, finally we got fast and uncontrolled data streaming on Flash (good for cam-streaming and such as well). I will not in this article explain codewise how to set up a connection using this technique but it will surely come as this is very much needed for the game. Now how can we use this? What do we need to consider when building a game?

Movement and actions

Actually, without really telling you, I’ve already tried to think multiplayer when it comes to this aspect already in the game. The first approach every mp programmer tries before knowing better (me included) is to just send the transformationdata to the other gamer and believing that just by sending my position and rotation values should be enough to have a great multiplayer game. The problem you will see is that transfers of data does not sometimes sync with the framerate of the game. Sending a package each and every frame will probably get heavy on the system and there is no saying that all those packages will be recieved at the same rate as they were sent.

Also you must consider the fact that the user on the other side might have a worse computer than you with a much lower framerate. Also there is this horrible lag-issue as it takes time for data to be transferred etc etc.

So we can easily say that we could use some tips here.

Predict the future

If we just could predict the actions of the other gamer we could render his unit due to these predictions. Now we at least try, right? First of all, instead of sending coordinates and rotations only we should also send some controls-data.
Actually the controlsdata is more important than the transformdata. Say you recieve some controldata. You loop through all your units on the screen, first your and transform it due to your controls and then the remote unit using the controldata you just recieved. What happens if you dont get any data next frame??? Well if you based the remote units movement only on coordinates it would get stuck on the same position as before now instead you can assume that if he held the forward button in the last millisecond, the best guess is that he/she probably does that now as well, so you continue calculate a new position using his/her last controldata.

Now whenever you recieve new data, use the new data both to control the unit (using unit data) and “tween” towards the absolutely correct positions and rotations. The result will be something similar to these two pictures:

mp_movement1mp_movement2

As you can see, on the top picture, using only transformdata will keep the unit stuttering to new positions on the frames it recieved new data meanwhile using controldata makes the unit move each frame BUT you will have some misplacement on the unit as it continues cusing the same controldata til a new is recieved. Therefor the controldata needs to be mixed with transformationdata so the most realistic movement can be achieved.

Time is relative

Until now, we have updated the speed and rotation each frame equally. Fine, this might work well in a singleplayer game as you are the only one competing against yourself but what if two players have different capacity on their computers? One computer is an ultramachine updating the game at 35 fps meanwhile the other one is going slow on 5 fps. This means that the fast comp will update the frame 35 times = moving it’s vehicle 7 times more often than the other one. Result is that the guy on the fast computer will have a faster moving unit. Also will he see the other unit move very strange as his computer will calculate the movements on the remote unit faster than it is actually being calculated on the slow computer.

Well you hear where this is leading… people will yell “CHEATERS” at each other and we will not have that cozy, nice and social game that we want. There are a few different solutions to this but the one I really recommend is called Delta Timing.

Move by time, not by frame

Delta Timing is to calculate how much the unit is transforming through time, and not static through each frametick. To achieve this you need to check the timespan between each movement. Let me give you some theory (yes, Ive done that this whole article)…

Assume you want the unit to move 10 pixels each frame. That is if your framerate is 30 FPS constantly. Then we have two good variables to start with, now lets calculate the time it really takes between one movement to the other. My pseudocode would look something like this:


{

//...here goes a lot of gamecode and nice stuff ...

currentTime  = currentTimeInMilliSeconds

timeItTookSinceLastTime = currentTime - lastTime

timeItShouldTake = 1000 / 30  // this gives how many milliSeconds it takes for one frame in 30FPS

timeScaler = timeItTookSinceLastTime / timeItShouldTake // give you a number that should be close to 1 if FPS is good.

lastTime = currentTime

}

With above code (no it’s not working if you try it, it’s just pseudo-mumbo-jumbo) you will get a variable timeScaler that will be lower than 1 if the FPS is actually going higher than 30 and higher than 1 if the FPS is dropping so this exact timeScaler must be used to compensate all speeddrops or speedboosts a FPS could get. So whenever you update, say a movement, each frame. instead of

unit.x += speed

with deltatiming you will now always use

unit.x += speed*timeScaler

Same goes with rotation, weapons, animations, cameramovement and everything that is speeded up or slowed down with your current fps.

Yes I know I haven’t given you a real line of code but the theory needs to be there before we actually create something with it. Now, swap your browser with your favourite AS3 IDE and start playing around with these things. I will soon implent it in the game code.

Cya

/Andreas