jellysmash_tuto01

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

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

Toby

jellysmash_tuto01_toby

This is how Toby looks like.

Toby needs to follow our mouse, start taking momentum when we press our mouse button and jump when we release it. We also need to show something on screen to represent it.

First thing first, let’s show Toby on screen

As I said before, ND2Dx now uses components. Components are objects that can be attached to a Node2D and enhance it.
In its very basic form, a Node2D doesn’t show anything on screen (it’s like a DisplayObjectContainer in Flash). In order to provide it with this functionality we need to use a special kind of component: Mesh2DRendererComponent.

A Mesh2DRendererComponent needs a material and a mesh. But what are those ?

  • a material basically handles how things are drawn on screen. In its simplest form, it takes a Mesh2D and sends its data (basically: triangles) to the gpu for processing. In more evolved forms (like Texture2DMaterial) it also contains a vertex and fragment program (shaders) and a Texture2D.
  • a mesh (Mesh2D) contains the vertexes (points or corners) of your triangles on which a color and/or a texture will be applied on. Triangles are the only thing your graphic card understands. It eats triangles, it dreams of triangles, it processes triangles… A mesh can represent anything but the most common one we will ever need is a quad, made up of two triangles. On it, you can apply any texture (with or without transparency) to represent anything you need. The cool thing about it is that you can reuse the same mesh for every single thing you want to show on screen, thus reducing memory usage.

So, how do we prepare a Node2D to make it show something on screen ? here goes this little piece of code that can be found in NodeUtil.as:

var node2D:Node2D = new Node2D();

// in order for a Node2D object to show something, it needs a Mesh2DRendererComponent
var mesh2DRendererComponent:Mesh2DRendererComponent = new Mesh2DRendererComponent();

// we then need to supply our Mesh2DRendererComponent with a material
// this one holds a texture as well as our vertex and fragment shader that tells the GPU how to draw things on screen
// you can easily modify and/or create new materials to suit your needs
var texture2DMaterial:Texture2DMaterial = new Texture2DMaterial();

// set material's texture
texture2DMaterial.setTexture(texture);

// set mesh renderer's material
mesh2DRendererComponent.setMaterial(texture2DMaterial);

// the last thing our Mesh2DRendererComponent needs is a mesh
// it contains the triangles data the GPU needs to draw something on screen
// we can use the same one for many different objects (less memory usage)
mesh2DRendererComponent.setMesh(Assets.quadMesh2D);

// finally add the Mesh2DRendererComponent to this object
node2D.addComponent(mesh2DRendererComponent);

What we are doing here is:

  1. create a Node2D
  2. create a Mesh2DRendererComponent
  3. create a Texture2DMaterial: it’s our material and this one needs a Texture2D
  4. set our texture to the material
  5. set our material to the mesh renderer component
  6. set our mesh to the mesh renderer component (in this case, we take an already pre-made quad mesh from our Assets class)
  7. and finally add the mesh renderer component to the node

I’m sure that this all look very complicated for just showing something on screen. But this is to show you what it actually takes to do so. This whole process can be all packed up in a helper class like I did or probably even simpler, in an object that extends Node2D and that integrates all of this for you (like Sprite2D did back in the days of ND2D).

Note to myself: I should probably add something like Sprite2D, it will make things much easier…

But what I really want to show through this is that this whole component, material, mesh and texture system allows us to be extremely flexible, saving memory where we can, reusing code, mix different components within a same node, change a mesh renderer component for another one right during the game in order to change the way an object is shown on screen (special effects), etc…

I believe this is a very flexible system and I’ll show you why more concretely later on.

But let’s get back to Toby and take the Toby.as file.

Toby will be made of several parts: a body (character) and a momentum circle (momentumCircle) that represents the area Toby will affect when landing.
First we create our momentum circle with the help of NodeUtil:

momentumCircle = NodeUtil.texturedQuadNode2D(TextureUtil.subTexture2DFromName("momentum_circle", Assets.jellySmashTexture2D));
addChild(momentumCircle);

TextureUtil.subTexture2DFromName() returns a sub-texture by its name from a texture atlas (textures contained in an atlas all have a name, commonly set by the software that created the atlas, in this case TexturePacker)

Then we create our character the same way we created our momentumCircle, only we specify another part of our atlas to be taken as a texture.

Make Toby follow the mouse

This is here things get interesting. I’m finally gonna be able to show the power of a component based system like the one used in ND2Dx. To make Toby follow the mouse, we will create a new component that will do just that.

Let’s take the FollowMouseComponent.as file.

It extends ComponentBase and overrides the public function step().
The step() function can be found in Node2D and in ComponentBase (and thus in every object extending those two) and is called at every single frame before it is drawn (drawNode() function). This is where your logic should take place.

Each component contains a node2D property. It is the parent Node2D that component is attached to. The step() function will never be called if the component is not attached to any Node2D. In that case, the node2D property would be set to null.

So how do we make it look towards the mouse ? right here with this piece of code :

override public function step(elapsed:Number):void 
{
	// get movement vector from our object (node2D) actual position to our mouse position
	vectorDirection.setVectorFromPoints(node2D.x , node2D.y, node2D.world.mouseX, node2D.world.mouseY);

	// if the distance between our mouse and our object is less than 1 pixel, then stop it where it is (in order to prevent weird rotations)
	if ( vectorDirection.length < 1 ) return;

	// normalize our vector (all values are between 0 and 1)
	vectorDirection.normalize();

	// so we can make it move at the speed we want
	node2D.x += vectorDirection.x * speed * elapsed * 140;
	node2D.y += vectorDirection.y * speed * elapsed * 140;

	// now let's make our object look towards the mouse

	// first we need to convert our rotation value to radians
	var rotationZRadian:Number = node2D.rotation * DEG2RAD;

	// get the rotation we want our object to have so it is looking towards our mouse
	var rotationTo:Number = Math.atan2(vectorDirection.y, vectorDirection.x);

	// now we could set our object rotation with "rotationTo" but that would look a bit too harsh for me
	// we can make our object rotate smoothly depending on its current speed

	// for that we first need to find the shortest rotation offset (from actual rotation value to the rotation value our object needs to be at)
	var rotationOffset:Number = Math.atan2(Math.sin(rotationTo - rotationZRadian), Math.cos(rotationTo - rotationZRadian));

	// and then add to our current rotation value the offset we just calculated (offset is first converted back from radians to degrees)
	// * 0.25 gives us a smooth transition when rotating (if you change it to 1, you'll see it will face the mouse without transition)
	node2D.rotation += (rotationOffset * RAD2DEG) * 0.25 * speed;
}

In resume :

  1. we get a directional vector (or movement vector) from where Toby is (the node2D property that references the object the component is attached to) to where it needs to go (our mouse)
  2. we check if the length of our vector (see distance in pixels) is smaller than 1 pixel: in that case, we stop everything in order to avoid weird movements and rotation (because the mouse is not as precise as a node2D for example, its position will always be absolute)
  3. we normalize our vector: all values are set between 0 and 1 (so we don’t have values whose scope/range is unknown)
  4. we make our node2D move in the direction set by our vector with a specific speed. The elapsed property provided by the step() function indicates the time that has passed since the last frame update. It allows us to have an object that will always move at the same speed, regardless of the frame rate our game is running at (so if our game suddenly has a drop in its frame rate, the movement will still be following its path at its rightful speed)
  5. then we convert node2D current rotation value from degrees to radians (sin, cos, atan2, etc… take values in radians)
  6. we calculate the rotation value our node2D needs to have in order to look towards our mouse (thanks to our directional vector)
  7. now we calculate the shortest rotation between the current rotation and the one we want our node2D to have. This will give us an “offset” or an amount of rotation the node2D needs to make to look at our mouse
  8. then we finally add this offset to the current rotation value of node2D (by converting it from radians to degrees first) multiplied by a randomly set value (here 0.25) to make it slowly and smoothly look towards our mouse, and not just straight away (change this value to 1.0 and you’ll see the difference)

Now we simply need to add this component to our Toby class and Toby will suddenly start following our mouse. We even could change this component a bit and provide with a property that will hold another Node2D object. That Node2D object would then act as the mouse. We would have then a component that makes an object follow another object.

The biggest benefit of this approach (component system) is re-usability. Now you can add this component to any object you want and it will start following your mouse. No more hard coded features that sits in a class and that you need to copy to another one because they can’t extend the same parent class with that same feature.
And also, you don’t need it to follow the mouse anymore ? deactivate it or remove it from its parent Node2D. As simple as that.

Now that it follows your mouse, let’s make it jump when your press your mouse button… right on the next page.

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

Pages: 1 2 3 4

Leave a Comment