Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

30. May 2017


Back in Part One of our look at the PlayCanvas HTML5 3D game engine, we started creating our simple bowling game.  We already created our lane and pins.  Now it is time to continue on and create our bowling ball.  Attached to our bowling ball will also be the script the controls the majority of our game.  Let’s jump back in.


Creating the Bowling Ball

This time, instead of importing a model, we are simply going to use a built in procedurally generated mesh.  Simply right click the Root in the Hierarchy view, select New Entity then Sphere.

image

Scale the newly created ball down and position it accordingly.

image

Under the Entity properties, I renamed it from Sphere to BowlingBall.  Right now our ball doesn’t look all that great.  Let’s go ahead and make it a bit shiny.  Right click in the asset area, select New Asset->Material.

image

Select the newly created material from the assets tab.  In the Material panel, I rename it to BowlingBallMaterial.  What I want to do is make the bowling ball a bit shiny.  I am going to leave it white, but if you want to change the color, do so under the Diffuse tab.  Next head on down to the Specular tab.  I used the following settings

image

Finally we need to apply the material to our bowling ball.  In the Hierarchy view, select the BowlingBall entity, under the Model panel, select our newly created Material.

image

Our bowling ball also needs to have a Collision shape and Rigid Body attached to it.

image

image

Be sure to set the Collision object to a Sphere and size the radius to be only slightly larger than our bowling ball model.


Now we have all the pieces needed for our game.  The pins, the lane and the bowling ball.  Now all we need to do is add some logic to the game.  This is done by attaching scripts to entities within our game.  In this case we are going to attach the logic to our bowling ball.


Scripting

With our BowlingBall entity active, add a component and select Script.

image


Scroll down to the Script panel, select Add Script->New Script.

image

Enter a name for the script.

image

Now click Edit Script to launch the script editor.

image

Now enter the following script:

var BallScript = pc.createScript('ballScript');

BallScript.attributes.add('speed', {
    type: 'number',
    default: -8
});

BallScript.prototype.initialize = function() {
    this.entity.collision.on('collisionstart', this.onCollisionStart, this);
    this.app.keyboard.on('keydown', this.onKeyDown, this);
    
    var pins = this.app.root.findByTag("Pin");
    this.startingPositions = [];
    this.startingRotations = [];
    for(var i = 0; i < pins.length; i++){
        this.startingPositions.push(pins[i].getLocalPosition().clone());
        this.startingRotations.push(pins[i].getLocalRotation().clone());
    }
    
    this.ballPosition = this.app.root.findByName("BowlingBall").getLocalPosition().clone();
    this.ballMoving = false;
};

BallScript.prototype.onCollisionStart = function (result) {
    
    if (result.other.rigidbody) {
        if(result.other.name == "BowlingPin")
            this.entity.sound.play("Hit");
    }
};

BallScript.prototype.onKeyDown = function(e) {
    if(e.key == pc.KEY_SPACE || e.key == pc.KEY_W){
        this.entity.rigidbody.applyImpulse(0,0, this.speed);
        this.ballMoving = true;
    }
    if(e.key == pc.KEY_R){
        var pins = this.app.root.findByTag("pin");
        for(var i = 0; i < pins.length; i++){
                pins[i].rigidbody.teleport(this.startingPositions[i],this.startingRotations[i]);
                pins[i].rigidbody.linearVelocity = pc.Vec3.ZERO;
                pins[i].rigidbody.angularVelocity = pc.Vec3.ZERO;
        }
        // now the ball
        this.entity.rigidbody.teleport(this.ballPosition);
        this.entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
        this.entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
        this.ballMoving = false;
    }
    if(e.key == pc.KEY_B){
         // Just the ball
        this.entity.rigidbody.teleport(this.ballPosition);
        this.entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
        this.entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
        this.ballMoving = false;
    }
    if(e.key == pc.KEY_LEFT || e.key == pc.KEY_A){
        this.entity.rigidbody.applyImpulse(-0.2,0,0);
    }
    if(e.key == pc.KEY_RIGHT || e.key == pc.KEY_D){
        this.entity.rigidbody.applyImpulse(0.2,0,0);
    }    
};

// update code called every frame
BallScript.prototype.update = function(dt) {
};


One thing you may notice in the script above is the definition of the speed attribute.  This exposes this variable back to the editor, like so:

image


You may notice the line of code in the script for playing audio when we collide with a pin:

    if (result.other.rigidbody) {
        if(result.other.name == "BowlingPin")
            this.entity.sound.play("Hit");
    }

Now we need to add this audio asset to our bowling ball.  First add the audio file as an asset.  Simply click add asset –> Upload, then upload a WAV file.  Now we need to add a sound component to our BowlingBall entity. 

image


Now scroll down to Slot 1 and add the asset you just uploaded and be sure to set name to Hit.

image


And done.  A complete, rather simple but fully functioning 3D bowling game in just a few minutes.


The Video

Programming , , ,

25. May 2017


I featured the PlayCanvas engine a couple years ago in this Closer Look review.  PlayCanvas is a 3D HTML5 powered game engine with a full suite of editing tools.  In the time since that initial review, PlayCanvas has advanced a great deal, so I have decided to revisit the engine.  Much of the engine has remained the same as it was, just generally improved all around.  Therefore I have decided instead of another review, I will do a hands on tutorial showing how to create a simple game using PlayCanvas.  In this case, a bowling game.  For Patreon backers all the assets used to make this tutorial are now available including Blender files, FBX, textures and more.

There is a video version of this tutorial available here.


Spoiler alert... here is the “game” we are about to create in this tutorial.

Click the window to focus.

Press spacebar or W to throw bowling ball.

Press left | A and right | D to move the bowling ball.

Press R to reset the table.

Press B to reset just the ball.


Creating your Project

Alright, lets jump in and build this game.  First head on over to http://playcanvas.com.  Now you either need to create an account or log in to your existing account.

image


A free account is sufficient for what we are doing here.  Once your account is created and you’ve logged in, click the New button

image


Select  Blank Project:

image


Now name your game and hit Create.  Unless you have a premium account, you can’t make private projects.

image


This will open your project after it’s created.  Now head into the settings.

image

You can set the resolution, change the name, description, etc...  but what we want to do in this case is enable physics for our project.

image

Next scroll to the bottom right corner and click the save button.

image


Now we are ready to fire up the editor.  Scroll back up and locate the Editor button.

image


Creating your Game

Welcome to the default editor!

image

Now that we are in the editor, it’s time to start creating our game.  You will notice on the left there is the Hierarchy, this is your games scene graph.

image

We have no need of the Box or Plane, simply right click each and delete them.

Now let’s get our assets into the game.  In Blender I created the bowling lane and pin textured meshes, then exported them as FBX ready for importing.  Let’s import them for use into our game, along with the required textures.  Locate the Assets window, click the + Icon, select Upload then select your FBX file(s).  Repeat the process with any texture maps you require.

image

This process will create a .json for each of your 3D models.  It also creates an empty material for both.

image


Now let’s set the texture for each material.  Simply select the Material, then in the Material tab to your right, scroll down and select Diffuse.  Click ... then select your texture map from the asset pane, like so:

image


I also have a normal map for the bowling lane, I repeat the above process, but instead of Diffuse I select Normal:

image


Creating the Bowling Lane

Ok, now that we have our raw assets and our textures linked, lets start creating some game objects.  Simply drag the lane into the scene like so:

CreateEnt


You will notice this creates a new entity in our scene graph:

image


PlayCanvas takes the traditional Entity Component model that is rapidly becoming the norm in the world of game development. 

image

You will notice that we have a Transform component, which enables us to position our entity in the world.  It also automatically created a Model component for us.  Now we want to add some physics to the game.  Since this is the lane, it wont be moving, so we create it as a static physics object.  To accomplish this we need to add two components, one for collisions that define the shape of our object to the physics engine, the second a RigidBody that defines our physical properties.  Let’s create the Collision shape first.

First click the Add Component button:

image

Then select Collision

image


Select Box then set the dimensions to surround your mesh as tightly as possible (note that PlayCanvas is a Y-up game engine):

image

image


Now Add Component again, this time selecting Rigid Body.  The default value should be Static, which is what we want.

image

Static means the object will be part of the physics simulation, but wont be moved by it.  Friction determines how well other objects slide over this surface, while Restitution determines how objects “bounce” off of it.


That’s it!  Our lane is completed.  Now it’s on to creating the bowling pin object.


Creating the Bowling Pins

Now it’s time to create the bowling pin.  Follow the exact same process you did earlier, drag the .json file onto the scene.  Position and scale it appropriately for your scene.

image

Just like last time, we want to create a collision component for this object.  This time instead of a cube, a cylinder is a better fit.

image

image

We also need to create a rigid body to control physics for this entity.  This time we create our entity as Dynamic ( static means stationary, dynamic is fully simulated, while kinematic takes part in the simulation but expects the movement to come directly from code, not the simulation).

image

Finally we are going to set a tag under the Entity section so we can identify all pins via code.  In the Tags section, enter “Pin” then hit enter to add a tag.  You can add multiple tags to an entity, a quick easy way to create ad-hoc groups of objects as we will see in a moment.

image


We are simulating 5 pin bowling here, so we need 4 more of them.  In the Hierarchy review, right click the pin and select Duplicate (not copy!).  This creates a copy of the entity and all of it’s components.

image

Repeat this process 3 more times, then go ahead and position the pins in a V like so.

image

Obviously if you are a 10 pin fan, other than being a heathen, you simply have to duplicate and position 5 more pins.


This post is getting pretty long so I’ve decided to split it into two parts.  Click here to continue on to part two.

Programming , ,

6. February 2017

 

In our previous tutorial we materials in our ongoing Babylon Tutorial Series but today we are going to take things a step higher up and let someone else worry about doing the work for us.  Today we will look at exporting 3D models from Blender for use in our game.  A number of exporters exist (3ds Max, FBX, Cheetah, Unity), but today we are specifically going to talk about using Blender.

First we need to download and install the plugin.  Fortunately they make a zip version available for download.  Head on over to https://github.com/BabylonJS/Babylon.js/blob/master/Exporters/Blender/Blender2Babylon-5.2.zip and click Download:

image

 

Save the zip file somewhere you will remember.  Now fire up Blender.  Select File->User Preferences…  then select the Add-Ons tab

image

 

Now choose Install From File… and select the newly downloaded zip.  Then in the filter area type “Bab” and Import-Export: Babylon.js should be available.  Simply check the checkbox to the right hand side to enable it.  We can now export our scene as a .babylon file for use in our game.  Simply select File->Export->Babylon.js:

image

 

There are no settings for the exporter, so simply pick your game asset directory and click Export Babylon.js Scene.  A .Babylon file and all of your textures will be created in your selected save location:

image

 

Now let’s look at the code required to load and display this model in our game:

    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);
        engine.enableOfflineSupport = false; // Dont require a manifest file
        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();


            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(0),
                    BABYLON.Tools.ToRadians(0),
                    10.0,BABYLON.Vector3.Zero(),scene);
            camera.attachControl(canvas,true);
            var light = new BABYLON.PointLight("PointLight",new BABYLON.Vector3(
            0,0,0),scene);
            light.parent = camera;
            light.intensity = 1.5;

            BABYLON.SceneLoader.ImportMesh("","","ShippingContainer.babylon",
            scene,function(newMeshes) {
                newMeshes.forEach(function(mesh){
                    mesh.rotation = new BABYLON.Vector3(BABYLON.Tools.ToRadians(
                    45),0,0);
                }                );
            });

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            scene.render();
        });

    });

 

The magic is done using the Y() function call.  You can load a specific asset from within the .babylon file by specifying it’s name, but in this case we just want the whole thing.  Import mesh has a callback when the mesh is loaded ( it’s loaded async, so you need to use this callback ) and it’s passed an array of Mesh objects.  We simply loop through this array ( in this example it’s only one item long, so we could have just as easily done newMeshes[0] and accomplished the same thing ).  Sometimes you will need to reposition your objects when loaded due to different coordinate systems, this example shows rotating each mesh by 45 degrees along the X axis.

 

This example uses the ShippingContainer blend file available as part of the Patreon dropbox assets, but you can use any applicable Blend file to create the example.  If you are a Patreon support (thanks by the way!) you can find this model in Art\Blender\ShippingContainer and the code is all available (including the exported Babylon file) in Tutorial Series\Babylon\5 - PartFive – Models.

 

When we run this example, we should see:

GIF

 

The Video

Programming, Art , , , ,

18. January 2017

 

In our previous tutorial we covered lighting in our ongoing Babylon Tutorial Series but the objects in our game are still remarkably drab.  A big part of this is the lack of materials applied to them.  In this tutorial we are looking at using the StandardMaterial which handles all the grunt work for you.  You can think of StandardMaterial as a container for several different kinds of textures (diffuse, opacity, etc. ) that can be applied to an object.  It also has some built in attributes such as diffuse (color), emissive (self lighting) and more.  Let’s start straight away with an example that we covered in a previous tutorial.  Applying a simply wireframe to our cube:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Common/Lib/babylon.max.js"></script>

    <style>

        #canvas {
            width:100%;
            height:100%;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();
            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);

            camera.attachControl(canvas,true);

            var material = new BABYLON.StandardMaterial("material1",scene);
            material.wireframe = true;
            box.material = material;

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            scene.render();
        });

    });
</script>
</body>
</html>

 

When you run it:

image

 

Simple enough.  We create a StandardMaterial, passing in it’s identity and the scene to create it in.  We set the materials wireframe property to true, then apply the material to our object’s material property.  Note each object can only have a single material, although a compound material exists if you need to mix multiple materials together.  Now let’s look at a slightly more colourful example, this time using more of the built in properties of StandardMaterial.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Common/Lib/babylon.max.js"></script>

    <style>

        #canvas {
            width:100%;
            height:100%;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();
            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);

            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);

            camera.attachControl(canvas,true);

            var light = new BABYLON.PointLight("pointLight",new BABYLON.Vector3(
            5,5,0),scene);
            light.diffuse = new BABYLON.Color3(1,1,1);



            var material = new BABYLON.StandardMaterial("material1",scene);
            material.diffuseColor = BABYLON.Color3.Blue();
            material.emissiveColor = BABYLON.Color3.Red();

            material.specularColor = BABYLON.Color3.Red();
            material.specularPower = 3;
            material.alpha = 1.0;
            box.material = material;

            var plane = BABYLON.Mesh.CreatePlane("plane", 10.0, scene, false, 
            BABYLON.Mesh.DOUBLESIDE);
            plane.material = new BABYLON.StandardMaterial("material2",scene);
            plane.material.diffuseColor = new BABYLON.Color3.White();
            plane.material.backFaceCulling = false;
            plane.position = new BABYLON.Vector3(0,0,-5);

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            var material = scene.getMeshByName("Box").material;
//            material.alpha -= 0.01;
//            if(material.alpha < 0) material.alpha = 1.0;
            scene.render();
        });

    });
</script>
</body>
</html>

Running this example results in:

image

 

Here you can see we’ve set the diffuse, emissive and specular values of the cube.  I also created a plane so you can see the emissive value of our cube has no effect on it.  The diffuse property can be thought of as the colour in the traditional sense.  Emissive on the other hand is a value for an internal light of the material, there aren’t actually that many emissive parallels in the real world, but some mosses and a few creatures have an emissive property to them.  Specular color determines how external light sources interact with the surface.  If you look at the commented code in the main loop you will also see commented code affecting the alpha channel of the material.  Alpha can be thought of transparency, with a value of 1 being fully opaque, while 0 is fully transparent.

What the majority of people think of when they work with materials is textures.  Textures are simply images that are applied the surface of an object like virtual wallpaper.  There are different types of textures as well, some effect the color of a surface, others affect the transparency or normals.  Here is an example:

<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();
            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);

            camera.attachControl(canvas,true);

            var light = new BABYLON.PointLight("pointLight",new BABYLON.Vector3(
            0,10,0),scene);
            light.parent = camera;
            light.diffuse = new BABYLON.Color3(1,1,1);


            var material = new BABYLON.StandardMaterial("material1",scene);

            material.diffuseTexture = new BABYLON.Texture("gfs.png",scene);
            material.bumpTexture = new BABYLON.Texture("gfs_normal.png",scene);
            material.roughness = 0.5;
            box.material = material;



            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){

            scene.render();
        });

    });
</script>

 

And when you run it:

image

 

This example uses two different textures, a diffuse texture:

gfs

 

And a normal map:

gfs_normal

 

For more details on Normal Maps, check this video on Normal Map 101.  Somewhat confusingly, BabylonJS refers to normal maps as bump textures.  This only scratches the surface of the material and texture options available, you also have options like ambient, opacity, reflection, light and specular textures, but you will find they almost all work exactly the same way.

Programming , , ,

2. January 2017

 

In our previous tutorial in the BabylonJS Tutorial Series we covered positioning a camera in our world.  There were still a few fundamental components missing, the top of which is lighting which we are going to cover today.  Lights are used to, predictably enough, illuminate your scene.  They interact with the color and materials on your various entities that compose your scene.  There are multiple different light types available in BabylonJS including the Point Light, Directional Light, Spot Light and Hemispherical light.  A point light is a single light source that radiates in all directions, like a naked lightbulb for example.  A directional light in a radiates just in the direction it is pointed and it goes on forever with no fall off basically illuminating everything in its path regardless to distance.  A spot light is similar to a directional light but it does fall off over a given distance and is cone shaped.  A flashlight is a classic example of a spot light, as of course is a spot (or search) light!.  A hemispherical light is generally used to represent an ever present ambient light source, the sun being perhaps the most common example.  You can also emit light from textures using their emission property, but we will cover that at a later point.  In this tutorial we are going to implement a point and a spot light.

 

There is an HD video version of this tutorial available here.

 

Let’s start with a point light.  It’s a simple light that radiates from a single point (thus the name) in all directions.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Common/Lib/babylon.max.js"></script>

    <style>

        #canvas {
            width:100%;
            height:100%;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();

            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);
            camera.attachControl(canvas,true);

            var light = new BABYLON.PointLight("pointLight",new BABYLON.Vector3(
            0,10,0),scene);
            light.diffuse = new BABYLON.Color3(1,0,0);


            scene.actionManager = new BABYLON.ActionManager(scene);
            scene.actionManager.registerAction(
                    new BABYLON.ExecuteCodeAction({ trigger:
                            BABYLON.ActionManager.OnKeyUpTrigger, parameter: " " 
                            },
                            function () {
                                light.setEnabled(!light.isEnabled());
                            }
                    ));

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            var light = scene.getLightByName("pointLight");
            light.diffuse.g += 0.01;
            light.diffuse.b += 0.01;
            scene.render();
        });

    });
</script>
</body>
</html>

 

There are a couple things illustrated in this example.  Creating a point light is done by calling new BABYLON.PointLight(), passing in the ID of the light, the position of the light in the world and finally the scene in which the light exists.  You can set the color of the light by setting it’s diffuse property, in this case we set it to full red only.  You will notice this example also shows a new concept in BabylonJS, the ActionManager.  This is a way of wiring code to specific events.  In this case we add some code that will be fired when the space key is pressed.  That function simply turns off and on the light source by calling setEnabled() passing a true or false value.  In the render loop we also slowly increase the lights green and blue components, so you can see the effect of diffuse lighting on the scene.  When you run this code you should see:

GIF

 

Lights are implemented as part of the GLSL shader process and the active lights in the scene are passed to each StandardMaterial in the scene.  By default the standard material is limited to a maximum of four active lights.  This value can be overridden using the maxSimultaneousLights property of the StandardMaterial, although this may have some impact on performance, especially on mobile targets.

 

Next lets look at implementing a spot light.  As with all things BabylonJS, the process is quite similar:

<script>
    window.addEventListener('DOMContentLoaded', function(){
        var canvas = document.getElementById('canvas');

        var engine = new BABYLON.Engine(canvas, true);

        var createScene = function(){
            var scene = new BABYLON.Scene(engine);
            scene.clearColor = new BABYLON.Color3.White();

            var box = BABYLON.Mesh.CreateBox("Box",4.0,scene);
            var camera = new BABYLON.ArcRotateCamera("arcCam",
                    BABYLON.Tools.ToRadians(45),
                    BABYLON.Tools.ToRadians(45),
                    10.0,box.position,scene);
            camera.attachControl(canvas,true);

            var light = new BABYLON.SpotLight("spotLight",new BABYLON.Vector3(0,
            10,0),new BABYLON.Vector3(0,-1,0),
                    BABYLON.Tools.ToRadians(45), // degrees the light fans out
                    0.1, // falloff/decay of the light over distance
                    scene);

            return scene;
        }

        var scene = createScene();
        engine.runRenderLoop(function(){
            var light = scene.getLightByName("spotLight");
            light.position.y -= 0.01;
            scene.render();
        });

    });
</script>

 

In this example we create the spot light with a call to new BABYLON.SpotLight, passing in the id, position, direction vector, the degrees or arc of the light cone, the rate the light falls off over distance and finally the scene to create the light in.  In this example instead of changing the color of the light each frame, we instead move it slightly.  Run this code and you should see:

GIF2

 

As the light is pulled back the fall off cone is quite prominently displayed.  Of course the lack of textures makes this example more than a bit stark, so that is what we will cover in the next tutorial.

 

The Video

Programming , , , ,

Month List

Popular Comments