jellysmash_tuto02

Jelly Smash part 2 : Game Tutorial for Flash-Stage3D & ND2Dx

0 Flares Twitter 0 Facebook 0 Google+ 0 Pin It Share 0 Email -- Filament.io 0 Flares ×

Hello, this is the second part of the Jelly Smash Game Tutorial.

Today (or tonight) we will dig a bit into behaviors in games development ! or how do we make something move without having it look like a dumb robot that only goes from A to B in a straight line without even worrying about whether it’s going through something else or not.

This will also be a good opportunity to use the component approach of ND2Dx.

The behavior we are going to implement is some sort of a wander behavior coupled with an avoidance technique. What we want is to make sure that our bugs are staying inside the game area, we don’t want them go outside of it and get lost. But we also want them to avoid each others so they don’t overlap. It also gives a more natural feeling of how a bug should behave.

Here is a preview of what we will be doing :

Get Adobe Flash player

 

Before we start, you can grab the complete source code with all the assets and their project files (FlashDevelop, TexturePacker, psd, …)

box_zipJellySmash Game Tutorial part.2 For Flash Stage3D and ND2Dx

Good. Now take the MoveRandomlyAndAvoidComponent.as file and let’s check it together.

Make it move randomly inside the game area

For this feature, I first tried an approach where the bugs would move randomly by rotating slowly towards a random rotation value that would change from time to time. But this ended up being a very complicated task as I wanted to make sure the bugs didn’t go outside of the game area, or at least not forever… I could have checked if they were still inside of it but as I wanted them to start from outside, it wouldn’t have worked. I also tried to add some kind of attraction force that would attract them towards the center of the game area whenever they would reach a certain distance from it. But they ended up circling… which was logical but still not what I wanted.

So I came up with a much simpler idea : pick a random point inside the game area and make them go towards it. This is done by the setNewTargetPoint() function :

// this sets a new target point
public function setNewTargetPoint():void
{
	// we make sure it stays inside our game scene
	targetPointX = Math.random() * node2D.stage.stageWidth;
	targetPointY = Math.random() * node2D.stage.stageHeight;

	// and randomly set a new duration
	currentDuraction = 0.0;
	duration = 2 + Math.random() * 4;
}

I believe everything is crystal clear here. The whole thing else happens in the step() function. I’ll post to complete source of this function here and explain it in two steps :

override public function step(elapsed:Number):void 
{
	// we set a new target point every "duration" (in seconds)
	currentDuraction += elapsed;
	// so when duration has reached, we set a new one
	if ( currentDuraction >= duration ) setNewTargetPoint();

	// get a vector between our current position and target point
	vector2D.setVectorFromPoints(node2D.x, node2D.y, targetPointX, targetPointY);

	// normalize so we have values between 0 and 1
	vector2D.normalize();

	// and substract our movement vector from it in order to have an offset, a difference between our current movement vector and the one that points towards our target point
	vector2D.substract(movementVector);

	// we add this "offset" vector to the movement vector, multiplied by a value (0.05 * speed * elapsed)
	// what (0.05 * speed * elapsed) is making its movement towards our target point more "smooth" and "transitional"
	// so it doesn't just look towards our target point but rather rotates and moves towards it based on its speed
	movementVector.add(vector2D.cross(0.05 * speed * elapsed));
	movementVector.normalize();

	// now we want each bug that is controled by this component not to overlap each others
	// so we want them to avoid each others in order to have a more "realistic" behavior
	// to do that we need to check the distance that separates every Bug to every other Bugs and make
	// them go away from each others based on the distance that separate them
	var child:Node2D = node2D.parent.childFirst;

	while (child)
	{
		// check if it's a Bug and that we are not gonna check against itself
		if ( child is Bug && child != node2D )
		{
			// get vector from the current bug to this one (the one holding this component)
			vector2D.setVectorFromPoints(child.x, child.y, node2D.x, node2D.y);

			// get a percentage of the distance between them
			prct = vector2D.length / distanceMaxBetweenBugs;
			if ( prct > 1.0 ) prct = 1.0;
			prct = 1 - prct;

			vector2D.normalize();

			// get offset (like earlier)
			vector2D.substract(movementVector);

			// and add this offset to the movement vector based on the percentage we go earlier
			// we also multiply the prct by 0.5 to make it turn more slowly
			// change this value to 10.0 for example and you'll see them turning much more rapidly
			movementVector.add(vector2D.cross(prct * 0.5));
			movementVector.normalize();
		}

		child = child.next;
	}

	// set position based on movement vector * speed * elapsed
	node2D.x += movementVector.x * speed * elapsed;
	node2D.y += movementVector.y * speed * elapsed;

	// and set our rotation based on movement vector (-90 degrees as the bug in the texture is looking towards the bottom)
	node2D.rotation = (MathUtil.RAD2DEG * Math.atan2(movementVector.y, movementVector.x)) - 90;
}

First step: everything is commented in the code but here is a resume of what happens there (maybe I’ll find something else to add) :

  1. We first increase the current duration and check if it has reached its limit. If yes, we set a new target point.
  2. Then we create a vector from where the bug is right now to the target point. It will give us the direction towards which our bug needs to move.
  3. We normalize it in order to have a value between 0 and 1. This is a very important step as our movementVector is also normalized and allows us to work with values within the same range (0 to 1).
  4. We then calculate the offset between the current movement vector (the one our bug is actually being moved by) and the vector that point towards the target point. It is a very useful value as it gives us the difference in direction the bug needs to add to its current direction in order to go towards the current target point.
  5. We can now multiply that offset by a value that will have as an effect to smooth the movement of our bug. The smaller the value is, the longer it will take our bug to move and rotate towards the target point.
  6. Then we add this multiplied offset to the current movement vector, giving us a new movement vector for our bug to move with.

This probably seemed long and complicated but in the end it’s only 7 lines of code. This was the first step of our behavior, now we want our bugs to avoid each others.

How to make them avoid each others

In this same step() function, we need to check whether a bug is going to collide with another one and how to avoid it. To make it simpler to understand, I’ll call “opponent” (“child” in the code) the current bug we are checking against and “bug” our current bug this component is attached to.

  1. We need to do this check for every bug in our scene. The ND2Dx framework uses linked lists instead of arrays or vectors to store its nodes and components. We all know that Vectors are faster than Arrays, but Linked List are even faster than Vectors (in some cases, and looping through a list is one of those cases). You can check this link from JacksonDunstan.com to get a nice performance comparison between arrays and linked list. Every child of a parent node is stored in a linked list instead of a vector or array in ND2Dx and this is how you can loop through a linked list.
  2. In the loop, we check if the current opponent is a Bug class and if it’s not our actual bug.
  3. We then calculate the distance between the opponent and our bug. Notice that we calculate a vector that goes from the opponent to our bug and not the opposite.
  4. We divide this distance by a value “distanceMaxBetweenBugs” that specifies the distance at which our bug starts to feel too close from its opponent (or danger zone). If the opponent goes pass that limit, our bug will start to avoid it.
  5. We then calculate a percentage (from 0 to 1) out of this distance: the close to 1 the more our bug needs to go away from our opponent.
  6. So we have a vector from the opponent to our bug (“vector2D” in the code) and a percentage representing the penetration value of our opponent inside our bug’s danger zone.
    jellysmash_tuto02_pic01
  7. What we do then is very simple: the more the opponent goes inside our danger zone, the more our bug will go towards the opposite direction of him (our “vector2D” in the code). To do that, we simply have to normalize that vector and multiply it by the percentage of penetration. As you can see, that percentage is itself being multiplied by 0.5. This will actually help to smooth to avoidance movement. The higher this value is, the quicker our bug is going to move away from the opponent.
  8. Then we add this new vector to our current bug’s movement vector.
  9. We do this for every bug in the scene and we are done.

Add this component to any node and it will start to move randomly and avoid other bugs.

The end

This closes the second part of the Jelly Smash Game Tutorial series.  You can see that this preview actually contains the code to smash some bugs but this is not advanced enough so this will be for part 3.

Hope you enjoyed it and learned a couple of things.

See you in part 3 !

 

0 Flares Twitter 0 Facebook 0 Google+ 0 Pin It Share 0 Email -- Filament.io 0 Flares ×

Leave a Comment