Actor plugins - 4 October 08

Using UMAJIN 2.0 we will create a lightweight simulation for simple AI actors.

I like to start with the graphics. Here are some 2d elements which can be combined like the puppy, or shown as states like the beach ball.

Now we flesh out the framework of the application. This will be split into two pieces - a host and plugin actors.

The host holds the overall scene and the plugin actors will contain the graphics and behaviors which make them unique and interesting cyber things.

I am going to show the simple ball actor code first.

[Actor]

define image puppet_ball	
	.filename = "puppets/ball1.png"

	.origin_x = .native_width / 2.0	
	.origin_y = .native_height / 2.0
end

This is an extremely simple actor. We start with the ball picture and set its origin to the centre of the image. However this does not give us any behavior at all. Simply making an instance of this class will result in a pretty boring actor.

Heartbeat

The next step is to look at our host script and add a heartbeat. This will be done using the on_tick() event umajin provides for every frame. We will augment this to include the time between this and the last frame so that the animation can appear the same on different speed machines.

[Host]

object items[]
int otime = system.time()

method on_tick()
	real timestep = (otime-system.time()) / 10.0
	otime = system.time()

	int a=0
	loop a<length(items)		
		raise items[a].tick(timestep)
		a++
	end
end

Now the beauty of this technique is that the raise command will try and find our special tick() method inside our actors. With this in mind I have added a few new properties to the ball. These are the x and y velocity, along with a damping factor which describes the amount of bounciness of our ball.

[Actor]

	property real vx = 0.0
	property real vy = 0.0
	property real damping = 0.9

You will notice the tick method takes a timestep as a parameter. This is the aforementioned measure of elapsed time since the last tick call. So on a slower machine we should see more motion between ticks.

[Actor]

	method tick(real timestep)
		//vertical motion
		.vy += gravity * timestep
		.y += .vy * timestep		
		//bounce
		if .y>worldy
			.y = worldy
			.vy = -.vy*.damping
			.vx = .vx*0.95
		end
		
		//bounce animation
		if .y>worldy-10 && .vy>4
			.filename = "puppets/ball2.png"
		else
			.filename = "puppets/ball1.png"
		end
		
		//horizontal motion
		.x += .vx * timestep
		//bounce
		if .x>worldx	
			.x = worldx
			.vx = -.vx
		end
		if .x<0
			.x = 0
			.vx = -.vx
		end
	end

This code is surprisingly short for quite a fun behavior. Essentially the ball will first add the acceleration from gravity to its vertical velocity. Then it will update its position. Then it will check if it is below ground level and reverse its velocity allowing for our damping factor (e.g. bounce).

The bounce animation changes the ball image when we are within 10 pixels of the ground and the velocity is still high (e.g. we are bouncing).

Finally the vertical velocity updates the position and the checks for bounces against the sides of the environment.




Who’s got the Brains?

The next trick is to give our actors more than just a heartbeat, let’s give them some brains. Now I do take offence to the term AI = Artificial Intelligence. I like to think of the normal techniques developers use as AI = Artificial Instinct. We do occasionally develop algorithms or simulations that appear emergent… but that’s just normally the human brain making patterns and connections where they don’t really existing. We are certainly not going to attempting to simulate real intelligence with neural networks or similar techniques in this tutorial. I’ll settle for a puppy who responds to our mouse click, and to the other objects in the world (the ball).

[Actor]

	method on_mouse_down(int x, int y, int mod)		
		if x>.native_width / 2.0
			.vx += .scale_width * 4
		else
			.vx -= .scale_width * 4
		end
		.vy += 1
	End

This code allows our puppy to feel the pinch. As we click on it there is a reaction where it jumps slightly in the air and away from our click. We do this by using the scale_width as an indication of direction. This means positive scale is facing forwards – and negative scale is facing backwards.

[Actor tick]

		//point the puppy the right way
		if .vx<0
			.scale_width = 0.5
		else
			.scale_width = -0.5
		end
	

Adding this code to the puppies tick even will result in ensuring the direction matches the current velocity and actual direction the puppy is travelling in.

Finally we can add a specific test to see if any balls are nearby.. and to react appropriately.

[Actor tick]

	//react to other object	
	int a=0
	loop a<length(items)
		if items[a].typeof()=="puppet_ball"
			puppet_ball b = items[a]
			if abs(b.x - .x - .scale_width*150) < 20 && abs(b.y - .y - 20) < 30
				b.vy += 10
				.head.angle = 40
				.head.angle = 0 in 500 tween linear
				b.vx += .scale_width
			end
		end
		a++
	end

This code essentially checks to see if the puppy is near an object of type puppet_ball. If this is the case then the ball is thrown up, slightly away and the puppies head is animated to perform a slight tilt – or headbut.




Any Courage?

OK let’s see if we can add some behaviors that are more than just reactions to us or other objects.

We need to give the puppy some sense of self. We will start with the puppy having a random action. Perhaps wander around the scene or sit down. The trick with these additional behaviors is we need to be able to recover from these states when it reaches an interaction.

Next we want to add a simple AI trick which is a level. These can be used to great effect in games like the Will Wright games the SIMs. The concept is that sims have levels like sleep, hunger or entertainment which you can effect through your actions.

Let’s make the puppy level ‘boredom’. If it is high, then you have not been playing with it enough. If it is low, then it is happy.

If we can come up with some actions for both extremes then we can create an actor who appears to be more alive. (e.g. sit down when bored and jump when excited)

p.s. Courage coming soon :-)

Cheers
  Dave