1. Trang chủ
  2. » Công Nghệ Thông Tin

Ebook 3D game programming for kids (Second edition): Part 2

269 6 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Project: The Purple Fruit Monster Game
Định dạng
Số trang 269
Dung lượng 2,53 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Continued part 1, part 2 of ebook 3D game programming for kids (Second edition) provide readers with content about: project - the purple fruit monster game; project - tilt-a-board; learning about javascript objects; project - ready, steady, launch; project - two-player games; getting code on the web;... Please refer to the part 2 of ebook for details!

Trang 1

This might seem like a simple game to write, but we’re going to use a lot of theskills and knowledge that we’ve been building up in the book And to get thejumping and rolling and capturing, we’re going to introduce a whole new level

of sophistication to our code This is going to be a fun one!

Trang 2

// The "scene" is where stuff in our game will happen:

③ var scene = new Physijs.Scene();

scene.setGravity(new THREE.Vector3( 0, -250, 0 ));

var flat = {flatShading: true};

var light = new THREE.AmbientLight('white', 0.8);

scene.add(light);

A setting that enables Physijs to decide when things bump into each other

“Worker” code that runs in the background, performing all of the physics

Trang 3

on that First, let’s convert from a 3D scene to a two-dimensional scene

Vectors Are Direction and Magnitude

We’re using THREE.Vector3 to set gravity We’regoing to use these a lot in this chapter If you saw the

The vector that describes gravity in this game points

in the negative Y direction (down) It has a highmagnitude (250), which means that things will falldown fairly quickly

Let’s Make 2D

The most important change to make for a 2D game is to use an orthographiccamera Back in Chapter 9, What’s All That Other Code?, we talked about twouses for these cameras: long distance views and 2D games We used an

orthographic camera for the long distances of space in Chapter 13, Project:

Trang 4

Still working above the START CODING line, comment out (or delete) the code forthe usual perspective camera Then add an OrthographicCamera as shown

» // var aspectRatio = window.innerWidth / window.innerHeight;

» // var camera = new THREE.PerspectiveCamera(75, aspectRatio, 1, 10000);

var renderer = new THREE.WebGLRenderer({antialias: true});

renderer.setSize(window.innerWidth, window.innerHeight);

» renderer.setClearColor('skyblue');

document.body.appendChild(renderer.domElement);

With that, we’re ready to start coding our jumping game

Trang 5

Let’s think about how we can organize our code To have made it this far in thebook, you’ve written a lot of code At times, it must have gotten difficult tomove through the code to see what you’ve done You’re not the first

programmer to run into this problem, and you won’t be the last Thankfully, youcan learn from the mistakes of programmers before you

Keep Your Code Organized

Programming is hard enough on its own Don’t make

it harder by writing messy code Organizing codedoesn’t matter too much with short programs Butcode grows as new stuff is added Organized code—indented and with functions defined in the order thatthey are called—is code that can grow

One of the easiest ways to organize code is to treat it a little bit like writing.When you write an essay, it helps to start with an outline After you have theoutline, you can fill in the details

When organizing code, it helps to write the outline first, then add the code below

it Since we’re programming, our outlines are also written in code Type in thefollowing, including the double slashes, below START CODING ON THE NEXT LINE

//var ground = addGround();

//var avatar = addAvatar();

//var scoreboard = addScoreboard();

This outline doesn’t include everything in the game, but it’s a lot of it The

ground will be the playing area The avatar is the player in the game The

scoreboard will keep score and display useful information

The double slashes at the beginning of each of those lines introduce a JavaScript

Trang 6

good thing since we haven’t defined those functions yet

Programmers call this “commenting out” code so it won’t run Programmers dothis for many reasons Here, we’re doing it to outline code without causing

errors

We’ll define these functions in the same order as they are in the code outline.This makes it easier to find code By looking at the code outline, we know thatthe addGround function will be defined before the addAvatar function, which will

be followed by addScoreboard() The faster we can find code, the faster we can fix

it or add things to it When you write a lot of code, tricks like this can really helpkeep things straight

After we build each function, we’ll come back to this code outline to remove thedouble slashes before the function call—we’ll “uncomment” the calls whenthey’re ready

Let’s get started writing the code that matches this outline

Trang 7

The first function call in our code outline is to the addGround function Just belowthe code outline (after the commented-out //addScoreboard() line), define thatfunction as follows:

function addGround() {

var shape = new THREE.BoxGeometry(2*w, h, 10);

var cover = new THREE.MeshBasicMaterial({color: 'lawngreen'});

var ground = new Physijs.BoxMesh(shape, cover, 0);

When creating a Physijs mesh, we can pass a third argument in addition to thegeometry and material That third argument is the object’s mass, which lets usmake things very heavy or very light In this case, we set the mass to a specialnumber: 0 The 0 means that the shape never moves If we didn’t set the

ground’s mass to 0, the ground would fall down like anything else!

Unlike regular meshes, the different shapes have different physical meshes Thelist includes Physijs.BoxMesh, Physijs.CylinderMesh, Physijs.ConeMesh,

Physijs.PlaneMesh, Physijs.SphereMesh, and for all other shapes, Physijs.ConvexMesh

Once this function is defined, we uncomment the call to addGround() in our codeoutline

» var ground = addGround();

//var avatar = addAvatar();

//var scoreboard = addScoreboard();

Trang 8

If everything is working, we should see green ground with blue sky in thebackground as shown in the figure.

Trang 9

In 3D programming, you can make simple graphics in two ways We’ll use both

in this game—one kind for the Purple Fruit Monster and the other kind for thefruit The simple graphic technique that we use for the Purple Fruit Monster is

called a sprite.

In the addAvatar() function, we create an invisible, physics-enabled box mesh,then we add the sprite to the box mesh Add this function below the addGround()function

function addAvatar() {

var shape = new THREE.CubeGeometry(100, 100, 1);

var cover = new THREE.MeshBasicMaterial({visible: false});

var avatar = new Physijs.BoxMesh(shape, cover, 1);

scene.add(avatar);

var image = new THREE.TextureLoader().load("imagesmonster.png");

var material = new THREE.SpriteMaterial({map: image});

var sprite = new THREE.Sprite(material);

sprite.scale.set(100, 100, 1);

avatar.add(sprite);

avatar.setLinearFactor(new THREE.Vector3(1, 1, 0));

avatar.setAngularFactor(new THREE.Vector3(0, 0, 0));

return avatar;

}

Sprites are graphics that always face the camera, which is exactly what we wantour 2D avatar to do in this game Sprites are super-efficient in graphics code.Any time we can use them, we make it much easier for the computer to do

everything it needs to do to keep the game running smoothly

Sprites start as tiny 1 by 1 things in a scene To see this sprite, we scale it by 100

in the X and Y directions—we stretch it in the left/right and up/down directions.The box mesh at the beginning of addAvatar() is doing all the work of falling

Trang 10

down, colliding with fruit, and colliding with the ground We give it a smallmass of 1 so it’ll be easy to push with the controls that we’ll add in a bit It’sinvisible because we set visible: false in its material, but it’s still there We add thesprite to the box mesh so we know where the avatar is.

The last thing we do in addAvatar() is to set the angular and linear “factors.”

These factors say how much an object can rotate or move in certain directions

By setting the angular factor to all 0s, we’re saying that our avatar cannot rotate

in any direction Even if it bounces off of spinning fruit, the avatar will alwaysstay straight up and down By setting the linear factor to two 1s and a 0, we’resaying that the avatar can move in the X and Y directions, but not the Z

direction In other words, we’re telling our 3D code that even though we’recreating a three-dimensional shape, it will only move in two dimensions

Move back up to the code outline and uncomment the addAvatar call

var ground = addGround();

» var avatar = addAvatar();

//var scoreboard = addScoreboard();

With that, we have a Purple Fruit Monster avatar…that’s stuck in the ground

Resetting the Position

We could have positioned the avatar in addAvatar(), but we just added it to thescene Instead, we’ll create a separate function to set the position Why use aseparate function? So we can re-use it!

Trang 11

we said that some functions tell part of a story The functions in our code outline

do that—they tell the story of setting up the game

Another kind of function is one that gets called over and over again Let’s createone of those functions that can start—or restart—the game by moving the avatar

to its start position Add the following after the addAvatar() function:

immediately change the avatar’s position back to start Setting dirtyPosition totrue lets us do it.

Be sure to add two underscores before dirtyPosition.It’s not _dirtyPosition The setting is dirtyPosition Ifyou use only one underscore, there will be no errors,but the movement controls won’t work

We move the avatar 60 percent of the way to the left: -0.6 times the distance fromthe center to the left edge of the window We also move it 200 above the ground.Finally we set the speed—the velocity—of the avatar We use a vector to start it

Trang 12

Add a call to reset() just below the code outline

var ground = addGround();

var avatar = addAvatar();

//var scoreboard = addScoreboard();

» reset();

That should leave our avatar hovering above the ground, to the left of the screen

Before adding controls to move the avatar, we have to tell our physics engine toactively simulate gravity and collisions

Actively Simulate Physics

As we did in Chapter 13, Project: Phases of the Moon, we put our game code—our physics simulation—inside a function named gameStep() Add it just belowthe reset() function

Trang 13

The setTimeout() inside gameStep() calls gameStep() after waiting for 1000/30

milliseconds—roughly 30 milliseconds That will ask the Physijs code to updatethe positions of everything in the scene every 30 milliseconds That sounds like alot, but it’s a nice balance It’s not so often that computers will start runningslow It’s often enough so that the updates look smooth when animated

Next, let’s add some controls to move the Purple Fruit Monster about

Movement Controls

To control the avatar, we use the keydown event listener that we saw in earlierchapters Add the following code below the animate function:

document.addEventListener("keydown", sendKeyDown);

function sendKeyDown(event) {

var code = event.code;

if (code == 'ArrowLeft') left();

if (code == 'ArrowRight') right();

if (code == 'ArrowUp') up();

if (code == 'ArrowDown') down();

if (code == 'Space') up();

if (code == 'KeyR') reset();

}

function left() { move(-100, 0); }

function right() { move(100, 0); }

function up() { move(0, 250); }

function down() { move(0, -50); }

Trang 14

The move() function is a little interesting The first two lines set the avatar’s Xscale if we’re moving in the X direction This flips the Purple Fruit Monster’simage to face left or right, depending on the direction in which we’re moving.The last two lines of move() push the avatar in the proper direction First, wecalculate the direction For example, when the left arrow key is pressed, the left()function is called The left() function calls move(-100, 0), which tells move to set x

to -100 and y to 0 The dir value is then set to a vector pointing (-100, 0, 0), which

is 100 to the left Applying a central impulse in that direction means a quickpush in the center of the avatar Pushing in the center of an object is easier thanpushing an edge Since we’re programmers and we like easy, we push in thecenter

With that, we should be able to hide the code and move the avatar up, down, left,and right

Trang 15

To complete the code outline, we next add the scoreboard Put this below theaddAvatar() function and above the reset() function

function addScoreboard() {

var scoreboard = new Scoreboard();

scoreboard.score();

scoreboard.help(

"Use arrow keys to move and the space bar to jump " +

"Don't let the fruit get past you!!!"

var ground = addGround();

var avatar = addAvatar();

» var scoreboard = addScoreboard();

reset();

You should now see a scoreboard showing 0 points

Trang 16

At this point, we’re done with the code outline and we have the basics for a solid2D game We have the playing area, the avatar (including controls), and a way tokeep score To make the game interesting, we still need to do a bit more work.Next, we’ll add gameplay We’re going to roll out some fruit and challenge theplayer to make the avatar eat as much as possible without touching the ground

Launching Fruit

First, to create fruit, add a function below the reset() function

function makeFruit() {

var shape = new THREE.SphereGeometry(40, 16, 24);

var cover = new THREE.MeshBasicMaterial({visible: false});

var fruit = new Physijs.SphereMesh(shape, cover);

fruit.position.set(w, 40, 0);

scene.add(fruit);

var image = new THREE.TextureLoader().load("imagesfruit.png");

cover = new THREE.MeshBasicMaterial({map: image, transparent: true});

shape = new THREE.PlaneGeometry(80, 80);

var picturePlane = new THREE.Mesh(shape, cover);

fruit.add(picturePlane);

fruit.setAngularFactor(new THREE.Vector3(0, 0, 1));

fruit.setLinearFactor(new THREE.Vector3(1, 1, 0));

above the ground

For the fruit image, we use the second way of adding simple, 2D graphics Afterloading the image, we map it into a basic material and add that to a simple plane

Trang 17

We again set angular and linear factors so that the fruit can only move and rotatetwo-dimensionally The angular factor only sets 1 for the Z axis This means thatthe fruit will be able to spin like the hands on an analog clock

Before returning the fruit from the function, we set an isFruit property That willhelp us later when we need to decide whether the avatar is colliding with theground or one of these pieces of fruit

To make sure that all of this is typed in correctly, add a call to makeFruit() afterthe function definition You should see the fruit on the very right edge of thescreen and there should be no errors in the JavaScript console If everything is

OK, remove the makeFruit() call

Next, we need to launch the fruit Add the launchFruit() function above the

makeFruit() function definition.

function launchFruit() {

var speed = 500 + (10 Math.random() scoreboard.getScore());

var fruit = makeFruit();

fruit.setLinearVelocity(new THREE.Vector3(-speed, 0, 0));

fruit.setAngularVelocity(new THREE.Vector3(0, 0, 10));

}

We start by making fruit with the makeFruit() function we just wrote We

calculate the speed as 500 plus a little extra The little extra is a random numberthat gets bigger as the score gets bigger A game should get harder the longer theplayer plays We make it harder by rolling the fruit faster and faster!

We apply the speed with setLinearVelocity() The motion needs to be from right toleft, so we set the X direction to the negative of the speed setting Last, we givethe fruit a little spin

We still need to call this function So, below the launchFruit() function, add twocalls

Trang 18

setInterval(launchFruit, 3*1000);

The first call to launchFruit() launches a single fruit right away Next, we use

setInterval() to keep calling launchFruit() every 3 seconds The setTimeout() functionthat we’ve already seen calls a function once after a delay The setInterval()

function does the same thing, but keeps calling over and over

With that, we have lots of fruit heading at the Purple Fruit Monster We can evenuse the keyboard controls to bounce off the fruit Next, we need to keep scorewhen that happens

Eating Fruit and Keeping Score

All the way at the bottom of our code—below the move() function, add code tosend collisions to the rest of our code

If the avatar collides with fruit, we add 10 points to the score, give the avatar alittle bump up, and remove the fruit from the screen (because the Purple FruitMonster ate the fruit)

Hide the code and try it out!

Game Over

We have all the elements we need for this game except one: a way to lose Let’sadd code so the game ends when the Purple Fruit Monster touches the ground

Trang 19

» var gameOver = false;

var ground = addGround();

var avatar = addAvatar();

var scoreboard = addScoreboard();

» "Purple Fruit Monster crashed! " +

» "Press R to try again."

» );

» }

}

If the object the avatar is colliding with is the ground, then the game is over Wealso update the scoreboard with a helpful message And note that we’re returningimmediately from the function if the game is over There’s no reason to checkfor collisions when the game is over!

Trang 20

Back up in the animate() function, add a check that returns right away—beforeanything is animated—if the game is over

var clock = new THREE.Clock();

var speed = 500 + (10 Math.random() scoreboard.getScore());

var fruit = makeFruit();

fruit.setLinearVelocity(new THREE.Vector3(-speed, 0, 0));

fruit.setAngularVelocity(new THREE.Vector3(0, 0, 10));

» var last = scene.children.length - 1;

» for (var i=last; i>=0; i ) {

» var obj = scene.children[i];

»

Trang 21

JavaScript—otherwise we risk skipping things that we didn’t mean to skip Thelist of things in the scene might be:

But wait! We skipped right over Fruit #2 By removing something at the

beginning of the list, we shift everything else in the list down by one By thetime the loop starts again, we have skipped an old fruit

We don’t have this problem doing the reverse Instead of starting with the first

Trang 23

Congratulations! You just wrote another game from scratch And it’s a fun andchallenging one You can still use other ways to improve things Some

This is your code, so make the game better as only you can!

Trang 24

If you’d like to double-check the code in this chapter, turn to Code: The Purple

try it on your own first

Trang 25

This was an impressive game to make In the upcoming chapters, we’ll practicethe physics skills that we developed here We’ll also build on the concept of agameStep function, which was fairly simple in this game But before that, whathigh score can you get with Purple Fruit Monster?

Copyright © 2018, The Pragmatic Bookshelf.

Trang 26

When you’re done with this chapter, you will

game

Have built a complete 3D mini-Understand how to build complex 3D game pieces

Be able to create cool particle effects like fire

Be able to combine shapes, materials, lights, and physics together in a game

Trang 28

A word to the wise: a ton of things are going on in this game, which means we’ll

be typing a lot of code To save time, we won’t talk much about ideas and

approaches that we introduced in earlier chapters If you haven’t already workedthrough those earlier chapters, coding this game may be frustrating!

Trang 29

In this project, we’ve chosen a template that already includes the setup neededfor the physics engine to work Double-check that you see the physics code inthe “Tilt-a-Board” project that you just created

// The "scene" is where stuff in our game will happen:

var scene = new Physijs.Scene();

scene.setGravity(new THREE.Vector3( 0, -100, 0 ));

var flat = {flatShading: true};

var light = new THREE.AmbientLight('white', 0.8);

scene.add(light);

Load the physics library

Tell the physics library where it can find additional help to detect collisions.Set up a worker to perform all of the physics calculations

Create a physics-enabled Physijs.scene

Trang 30

⑤ Enable gravity.

Lights, Camera, Shadows!

Most of the light in this game will come from point lights that can cast shadows

So let’s dial back the ambient light brightness at the top of the code from 0.8 to0.2

var light = new THREE.AmbientLight('white', 0.2);

To get the best view of this game, we’ll want the camera a little above and backfrom the game board, but still looking down at the center of the scene We alsoneed to enable shadows in the renderer as we did in Chapter 12, Working with

Trang 31

In addition to lights, this game will have a ball, a game board, and a goal Let’sstart with the following code outline, including the double slashes:

//var lights = addLights();

//var ball = addBall();

//var board = addBoard();

//var goal = addGoal();

Just as we did in Chapter 14, Project: The Purple Fruit Monster Game, we’lluncomment these function calls as we define the functions

Add Lights

Before doing anything else, let’s add some lights to the scene

Below the commented-out code outline, add the following function definition ofaddLights:

function addLights() {

var lights = new THREE.Object3D();

var light1 = new THREE.PointLight('white', 0.4);

Trang 32

Now that we’ve added the function definition, uncomment the call to addLights inthe code outline

» var lights = addLights();

//var ball = addBall();

//var board = addBoard();

//var goal = addGoal();

Add the Game Ball

Add the addBall function below the function definition for addLights

function addBall() {

var shape = new THREE.SphereGeometry(10, 25, 21);

var cover = new THREE.MeshPhongMaterial({color: 'red'});

After finishing the function, uncomment the call to addBall() in the code outline

var lights = addLights();

» var ball = addBall();

//var board = addBoard();

//var goal = addGoal();

Since the ball is physics-enabled, it falls right out of the scene never to be seenagain as shown in the figure

Trang 33

var shape = new THREE.CubeGeometry(50, 2, 200);

var beam1 = new Physijs.BoxMesh(shape, cover, 0);

shape = new THREE.CubeGeometry(200, 2, 50);

var beam3 = new Physijs.BoxMesh(shape, cover, 0);

beam3.position.set(40, 0, -40);

beam3.receiveShadow = true;

beam1.add(beam3);

Trang 34

var beam4 = new Physijs.BoxMesh(shape, cover, 0);

on them

One thing that’s new is the 0 in each of the BoxMeshes

var beam1 = new Physijs.BoxMesh(shape, cover, 0);

As we saw in Chapter 14, Project: The Purple Fruit Monster Game, the 0 tellsthe physics library that the board does not move Without the 0, our game boardwould fall right off the screen just like the ball does

Uncomment the call to addBoard in the code outline

var lights = addLights();

var ball = addBall();

» var board = addBoard();

//var goal = addGoal();

With that, the ball should fall right through the middle of the game board

Trang 35

Reset the Game

When the game starts—or restarts—the game board should have a slight tilt andthe ball should fall down on the edge of the far left beam Add the followingreset() function below the addBoard() function:

function reset() {

ball. dirtyPosition = true;

ball. dirtyRotation = true;

ball.position.set(-33, 200, -65);

ball.setLinearVelocity(new THREE.Vector3(0, 0, 0));

ball.setAngularVelocity(new THREE.Vector3(0, 0, 0));

Trang 36

Add a call to the reset() function below the code outline

var lights = addLights();

var ball = addBall();

var board = addBoard();

//var goal = addGoal();

» reset();

Our code outline now adds lights, the game ball, and the game board Then itresets everything to the start position With that, the ball should start on the back

Trang 37

OK, we have our ball and game board and a way to start and restart the game.Before adding the goal, let’s add keyboard controls for the game board

var code = event.code;

if (code == 'ArrowLeft') left();

if (code == 'ArrowRight') right();

if (code == 'ArrowUp') up();

if (code == 'ArrowDown') down();

}

By now we’re familiar with using JavaScript keyboard events to control

gameplay like this Here, we’re calling functions to tilt the game board left,

Trang 38

sendKeyDown() function

function left() { tilt('z', 0.02); }

function right() { tilt('z', -0.02); }

function up() { tilt('x', -0.02); }

function down() { tilt('x', 0.02); }

function tilt(dir, amount) {

We already know dirtyRotation from the reset() function We have to set it herebecause otherwise the board doesn’t move Remember when we added the 0 tothe BoxMesh in addBoard()? That 0 says the board doesn’t move or rotate…unless

we set a dirty property

What’s really sneaky in tilt is board.rotation[dir] When the left function is called,

it calls tilt, setting the dir argument to ’z’ Since dir is ’z’, setting board.rotation[dir]

is the same thing as setting board.rotation[’z’] This is something new! We’ve seenstuff like board.rotation.z, but we’ve never seen square brackets and a string likethat

Well, it turns out that board.rotation[’z’] is the same as board.rotation.z JavaScriptsees both as changing the z property of the rotation Using this trick, we writejust one line that can update all different directions in tilt

board.rotation[dir] = board.rotation[dir] + amount;

Without a trick like that, we would probably have to use four different if

statements So we lazy programmers like this trick!

Give the game board a try! You should be able to tilt it left, right, up, and downusing the arrow keys

Trang 39

function addGoal() {

shape = new THREE.CubeGeometry(100, 2, 100);

cover = new THREE.MeshNormalMaterial({wireframe: true});

var goal = new Physijs.BoxMesh(shape, cover, 0);

You might have noticed that we set wireframe to true

Trang 40

Normally you should remove the wireframe property

in finished game code (you can remove the enclosingcurly braces, too) In this game, it probably makesthe most sense to change wireframe: true to visible: false

so that the goal is invisible to the player

Before we get to the collision detection for winning the game, let’s add anotherfunction to add a “goal light.” This light will do two things: highlight the goalfor the player and flash when the player wins the game

Add the addGoalLight() function below the reset() function

var goalLight1, goalLight2;

function addGoalLight(){

var shape = new THREE.CylinderGeometry(20, 20, 1000);

var cover = new THREE.MeshPhongMaterial({

Ngày đăng: 20/12/2022, 13:01