• Register
Post tutorial Report RSS Physics Wall

We introduce basic entities and physic concepts in Wave Engine to make all them work together by creating a world with a ground and a wall with physic interaction.

Posted by on - Basic Client Side Coding


Objectives

We introduce basic entities and physic concepts in Wave Engine to make all them work together by creating a world with a ground and a wall with physic interaction.


Create Project

So create a new Game Project from the Wave Engine template, and name it PhysicWall:



Create the camera

First thing we are going to need is a camera to see the world from. So, go to MyScene.cs, remove the Sample Test region and add these usings :

csharp code:

csharp code:
using WaveEngine.Common.Math;
using WaveEngine.Components.Cameras;

Create a camera adding this code at the end of the CreateScene() method.:

csharp code:

csharp code:
FreeCamera camera = new FreeCamera("MainCamera", new Vector3(0, 10, 20), new Vector3(0, 5, 0))
{
    BackgroundColor = Color.CornflowerBlue
};

EntityManager.Add(camera);

We are creating a camera, adding it to the EntityManager, as the camera is an Entity, and set it as the active camera.

The FreeCamera default behavior allow us to move in our world by using the keys a,w,s,d and if we press the left mouse button we can look around.


Create the ground

We are going to create the ground for our world. It will consist in a cube, with a 3D transformation that set his height to 1. But first, we need to add these usings to MyScene.cs file:

csharp code:

csharp code:
using WaveEngine.Framework.Graphics;
using WaveEngine.Framework.Physics3D;
using WaveEngine.Components.Graphics3D;
using WaveEngine.Materials;

Now copy the next code to create the ground at the end of CreateScene() method:

csharp code:

csharp code:
Entity ground = new Entity("Ground")
.AddComponent(new Transform3D() { Position = new Vector3(0, -1, 0), Scale = new Vector3(100, 1, 100) })
.AddComponent(new BoxCollider())
.AddComponent(Model.CreateCube())
.AddComponent(new RigidBody3D() { IsKinematic = true })
.AddComponent(new MaterialsMap(new BasicMaterial(Color.White)))
.AddComponent(new ModelRenderer());
EntityManager.Add(ground);

The Transform3D() component indicates the ground is positioned in the world origin in X and Z axis, but -1 in Y axis. And the Scale set the size of the ground to 100 in width, 1 in height and 100 in length, this Scale is related to the Model.CreateCube() component that create a 1x1x1 cube by default.

The BoxCollider() sets the very basic collision algorithm that will apply to our ground, that related with the RigidBody3D() sets the default properties for the physic calculations, but modifies the default IsKinematic property to true.

The MaterialsMap component sets the texture or list of textures that the ground will have. By now, we only set a plane color Color.gray. Later we will apply a texture to it.

Now, if you build and run you will get this:

Now you can move in the world due to the FreeCamera default behavior by using w,a,s,d to move and the left mouse button to look around.


Create a brick

Now, we will repeat the same process to create a brick as we did with the ground, so to create a simple brick use this code at the end of CreateScene() method:

csharp code:

csharp code:
Entity brick = new Entity("brick")
    .AddComponent(new Transform3D() { Position = new Vector3(  0.5f,5f ,0), Scale = new Vector3(1f, 0.5f, 0.5f)})
    .AddComponent(new MaterialsMap(new BasicMaterial(Color.Red)))
    .AddComponent(Model.CreateCube())
    .AddComponent(new BoxCollider())
    .AddComponent(new RigidBody3D() { Mass = 100 })
    .AddComponent(new ModelRenderer());
EntityManager.Add(brick);

When you build and run you will get a brick falling to the ground:

Use your keyboard and mouse to move the camera and see the 3D brick.


Create the wall

Now, we will create a wall with basic bricks, so encapsulate the brick creation code with this new function:

csharp code:

csharp code:
private Entity CreateBox(string name, Vector3 position, Vector3 scale, float mass)
{
    Entity primitive = new Entity(name)
        .AddComponent(new Transform3D() { Position = position, Scale = scale })
        .AddComponent(new MaterialsMap(new BasicMaterial(Color.Red)))
        .AddComponent(Model.CreateCube())
        .AddComponent(new BoxCollider())
        .AddComponent(new RigidBody3D() { Mass = mass })
        .AddComponent(new ModelRenderer());
    return primitive;
}

Now we will replace the first block code with two for loops that will create a wall, so place this code where the simple box was:

csharp code:

csharp code:
int width = 10;
int height = 10;
float blockWidth = 2f;
float blockHeight = 1f;
float blockLength = 1f;

int n = 0;

for (int i = 0; i < width; i++)
{
    for (int j = 0; j < height; j++)
    {
        n++;
        var toAdd = CreateBox("box" + n, new Vector3(i * blockWidth + .5f * blockWidth * (j % 2) - width * blockWidth * .5f,
                                                        blockHeight * .5f + j * (blockHeight),
                                                        0),
                                                        new Vector3(blockWidth, blockHeight, blockLength), 10);
        EntityManager.Add(toAdd);
    }
}

Just not to get lost, the MyScene.cs code look like this:

csharp code:

csharp code:
#region Using Statements
using System;
using WaveEngine.Common;
using WaveEngine.Common.Graphics;
using WaveEngine.Common.Math;
using WaveEngine.Components.Cameras;
using WaveEngine.Components.Graphics2D;
using WaveEngine.Components.Graphics3D;
using WaveEngine.Framework;
using WaveEngine.Framework.Graphics;
using WaveEngine.Framework.Resources;
using WaveEngine.Framework.Services;
using WaveEngine.Common.Math;
using WaveEngine.Components.Cameras;
using WaveEngine.Framework.Graphics;
using WaveEngine.Framework.Physics3D;
using WaveEngine.Components.Graphics3D;
using WaveEngine.Materials;
#endregion

namespace PhysicWallProject
{
    public class MyScene : Scene
    {
        protected override void CreateScene()
        {
            //Insert your scene definition here.

            FreeCamera camera = new FreeCamera("MainCamera", new Vector3(0, 10, 20), new Vector3(0, 5, 0))
            {
                BackgroundColor = Color.CornflowerBlue
            };

            EntityManager.Add(camera);

            Entity ground = new Entity("Ground")
            .AddComponent(new Transform3D() { Position = new Vector3(0, -1, 0), Scale = new Vector3(100, 1, 100) })
            .AddComponent(new BoxCollider())
            .AddComponent(Model.CreateCube())
            .AddComponent(new RigidBody3D() { IsKinematic = true })
            .AddComponent(new MaterialsMap(new BasicMaterial(Color.White)))
            .AddComponent(new ModelRenderer());
            EntityManager.Add(ground);

            int width = 10;
            int height = 10;
            float blockWidth = 2f;
            float blockHeight = 1f;
            float blockLength = 1f;

            int n = 0;

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    n++;
                    var toAdd = CreateBox("box" + n, new Vector3(i * blockWidth + .5f * blockWidth * (j % 2) - width * blockWidth * .5f,
                                                                    blockHeight * .5f + j * (blockHeight),
                                                                    0),
                                                                    new Vector3(blockWidth, blockHeight, blockLength), 10);
                    EntityManager.Add(toAdd);
                }
            }
        }

        protected override void Start()
        {
            base.Start();

            // This method is called after the CreateScene and Initialize methods and before the first Update.
        }

        private Entity CreateBox(string name, Vector3 position, Vector3 scale, float mass)
        {
            Entity primitive = new Entity(name)
                .AddComponent(new Transform3D() { Position = position, Scale = scale })
                .AddComponent(new MaterialsMap(new BasicMaterial(Color.Red)))
                .AddComponent(Model.CreateCube())
                .AddComponent(new BoxCollider())
                .AddComponent(new RigidBody3D() { Mass = mass })
                .AddComponent(new ModelRenderer());
            return primitive;
        }
    }
}
 

Now if we build and run we will get this:

But we are going to make it a little more fun, so create a method to return random colors like this:

csharp code:

csharp code:
private Color GetRandomColor()
{
    var random = WaveServices.Random;
    return new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), 1f);
}

And in the CreateBox() method change the line that sets the material for the box:
csharp code:

csharp code:
.AddComponent(new MaterialsMap(new BasicMaterial(Color.Red)))

With this one:

csharp code:

csharp code:
.AddComponent(new MaterialsMap(new BasicMaterial(GetRandomColor())))

Now if we build and run, a very funny wall will appear:

Create the ball

We are going to create a ball every time users click the left mouse button or tap the screen that will interact with the wall.

We need to add a Behavior. A Behavior is a component that has a logic update. As a component a behavior can be added to an Entity, and it provides an Update method to modify the Entity logic which is associated to, so create a new class to the PhysicWallProject project and name it as FireBehavior.

Add the usings:

csharp code:

csharp code:
using WaveEngine.Framework;
using WaveEngine.Framework.Graphics;

So we can define a behavior, and make de FireBehavior class to inherit from Behavior:

csharp code:

csharp code:
public class FireBehavior:Behavior

Now add these class variables:

csharp code:

csharp code:
private bool pressed;

Entity sphere;

[RequiredComponent]
public Camera3D Camera;

The pressed variable will be useful to check if left mouse button has been pressed or screen has been tapped. Sphere entity is the ball that will be thrown. Camera is the component we will associate this behavior and with [RequiredComponent] we indicates that is a component requirement. So if we do not associate this behavior to a camera, an exception will be thrown.

This is the default constructor:

csharp code:

csharp code:
public FireBehavior()
    : base("FireBehavior")
{
    Camera = null;
}


Default constructor just calls the base constructor with a string representing the behavior name, and initializes Camera variable.

We need to add these usings:

csharp code:

csharp code:
using WaveEngine.Framework.Services;
using WaveEngine.Components.Graphics3D;
using WaveEngine.Common.Math;
using WaveEngine.Materials;
using WaveEngine.Common.Graphics;
using WaveEngine.Framework.Physics3D;

We are going to add the logic to the Update method by overriding it in this way:
csharp code:

csharp code:
protected override void Update(TimeSpan gameTime)
{
    var touches = WaveServices.Input.TouchPanelState;

    if (touches.Count > 0)
    {
        if (!pressed)
        {
            pressed = true;

            if (sphere == null)
            {
                sphere = new Entity("ball")
                .AddComponent(new Transform3D() { Scale = new Vector3(2) })
                .AddComponent(new MaterialsMap(new BasicMaterial(Color.Gray)))
                .AddComponent(Model.CreateSphere())
                .AddComponent(new SphereCollider())
                .AddComponent(new RigidBody3D() { Mass = 3, EnableContinuousContact = true })
                .AddComponent(new ModelRenderer());
                EntityManager.Add(sphere);

            }

            RigidBody3D rigidBody = sphere.FindComponent<RigidBody3D>();
            rigidBody.ResetPosition(Camera.Position);
            var direction = Camera.LookAt - Camera.Position;
            rigidBody.ApplyLinearImpulse(500 * direction);
        }
    }
    else
    {
        pressed = false;
    }
}

With this method we are modifying the Update method from the Entity associated. First we need access to the TouchPanelState that represents the device touch panel state. WaveServices.Input gave access to all device input sensors as compass, accelerometer, etc. In this case we need access to touch panel state.

Next we control the touch and click gestures just not to initialize and throw the ball every time Update method is executed, so if the ball is not initialized we create one with red color, scale it to 2 in 3D, use a SphereCollider and set the RigidBody3D parameters to calculates collision.

Once the ball has been initialized and Update method detect it is time to throw a ball, it sets the position as the Camera position and apply an impulse with ApplyLinearImpulse from the rigidBody sphere component.

With this code, we have a behavior that makes every time we click or tap the screen, a ball will be thrown.

Last thing we have to do is to associate this behavior to our camera, so go to MyScene.cs file, locate the camera initialization and modify the initialization code to look like this

csharp code:

csharp code:
FreeCamera camera = new FreeCamera("MainCamera", new Vector3(0, 10, 20), new Vector3(0, 5, 0))
{
    BackgroundColor = Color.CornflowerBlue
};

camera.Entity.AddComponent(new FireBehavior());

EntityManager.Add(camera);

You can build/run and interact with the wall:



Adding textures

It is time to apply some texture to our object. You can search the web, create yours or use the textures located in Textures tutorial directory.

We need to add a .png and a .fbx file located at the Resources tutorial folder into the Asset Exporter. Click on the Resources.weproj file placed into the PhysicWallProject project:

Click on the Add button:

Navigate to the files we want and click "Open":

Click on File/Save Project and close the Asset Exporter. Now the asset files are included into the project.

To apply the texture amd the model to our scene, lets modify first the brick BasicMaterial by modifying the line in CreateBox() method:

csharp code:

csharp code:
.AddComponent(new MaterialsMap(new BasicMaterial(GetRandomColor())))

By getting the brickTexture texture

csharp code:

csharp code:
.AddComponent(new MaterialsMap(new BasicMaterial("Content/brickTexture.wpk")))

Finally, modify the line:

csharp code:

code:
.AddComponent(Model.CreateCube())

By getting the brick texture:

csharp code:

csharp code:
.AddComponent(new Model("Content/brick.wpk"))

Final result:

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: