针小孩的 3D 游戏编程(3D Game Programming for Kids)


Early praise for 3D Game Programming for Kids I was thrilled how much my son got into programming as a result of this book. He spent hours with it and was often surprised when his “screen time” was over because the time just flew by. Although the book doesn’t delve into the fundamentals of software programming (how computers store and retrieve data), kids get to see the results of their programming right away—the pictures and animations that they created—and are hooked into wanting to learn more. ➤ Mark Musante, professional software designer I would recommend this book to anyone my age that is interested in coding or tech- nology. It was very helpful and insightful about the basic (and the more complex) parts of standard coding. This book would be great for anyone looking to jump head-first into coding. ➤ Hana B., age 15 This is the best book a beginning programmer could get. It teaches programming concepts in fun and entertaining ways. This book is a great start in learning to program! ➤ Alec M., age 13 It has been great fun reading this book. It takes me back to when I fell in love with programming. After having spent the past twenty years programming solutions on the server side, I find this 3D book a welcome diversion that offers new concepts and ideas with instant visual feedback! I hope the book finds its way into the hands of an inquis- itive child who gets hooked on computer programming like I did. ➤ Darren Hunt, director of Algorithmic Solutions Limited 3D Game Programming for Kids Create Interactive Worlds with JavaScript Chris Strom The Pragmatic Bookshelf Dallas, Texas • Raleigh, North Carolina Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and The Pragmatic Programmers, LLC was aware of a trademark claim, the designations have been printed in initial capital letters or in all capitals. The Pragmatic Starter Kit, The Pragmatic Programmer, Pragmatic Programming, Pragmatic Bookshelf, PragProg and the linking g device are trade- marks of The Pragmatic Programmers, LLC. Every precaution was taken in the preparation of this book. However, the publisher assumes no responsibility for errors or omissions, or for damages that may result from the use of information (including program listings) contained herein. Our Pragmatic courses, workshops, and other products can help you and your team create better software and have more fun. For more information, as well as the latest Pragmatic titles, please visit us at http://pragprog.com. The team that produced this book includes: Fahmida Rashid (editor) Potomac Indexing, LLC (indexer) Candace Cunningham (copyeditor) David J Kelly (typesetter) Janet Furlow (producer) Juliet Benda (rights) Ellie Callahan (support) Copyright © 2013 The Pragmatic Programmers, LLC. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Printed in the United States of America. ISBN-13: 978-1-937785-44-4 Encoded using the finest acid-free high-entropy binary digits. Book version: P1.0—September, 2013 For Greta, so that she knows she can do anything. Contents Acknowledgments ........... xiii Introduction ............. xv 1. Project: Creating Simple Shapes ........ 1 Programming with the ICE Code Editor 11.1 1.2 Making Shapes with JavaScript 4 1.3 Animating the Shapes 14 1.4 The Code So Far 15 1.5 What’s Next 15 2. Playing with the Console and Finding What’s Broken ... 17 Getting Started 172.1 2.2 Opening and Closing the JavaScript Console 18 2.3 Debugging in ICE: The Red X 19 2.4 Debugging in ICE: The Yellow Triangle 19 2.5 Debugging in the Console 20 2.6 Recovering When ICE Is Broken 23 2.7 What’s Next 24 3. Project: Making an Avatar ......... 25 Getting Started 263.1 3.2 Making a Whole from Parts 26 3.3 Breaking It Down 28 3.4 Adding Feet for Walking 29 3.5 Challenge: Make the Avatar Your Own 31 3.6 Doing Cartwheels 32 3.7 The Code So Far 34 3.8 What’s Next 34 4. Project: Moving Avatars .......... 35 Getting Started 354.1 4.2 Building Interactive Systems with Keyboard Events 36 4.3 Converting Keyboard Events into Avatar Movement 37 4.4 Challenge: Start/Stop Animation 39 4.5 Building a Forest with Functions 40 4.6 Moving the Camera with the Avatar 43 4.7 The Code So Far 47 4.8 What’s Next 47 5. Functions: Use and Use Again ........ 49 Getting Started 495.1 5.2 Understanding Simple Functions 53 5.3 When Things Go Wrong 54 5.4 Weird Tricks with Functions 57 5.5 The Code So Far 58 5.6 What’s Next 58 6. Project: Moving Hands and Feet ........ 59 Getting Started 596.1 6.2 Moving a Hand 59 6.3 Swinging Hands and Feet Together 63 6.4 Walking When Moving 63 6.5 The Code So Far 66 6.6 What’s Next 66 7. A Closer Look at JavaScript Fundamentals ..... 67 Getting Started 677.1 7.2 Describing a Thing in JavaScript 67 7.3 Changing Things 69 7.4 Repeating and Skipping Code with while and if 74 7.5 Listing Things 77 7.6 What Makes JavaScript Different 78 7.7 What’s Next 78 8. Project: Turning Our Avatar ......... 79 Getting Started 798.1 8.2 Facing the Proper Direction 79 8.3 Breaking It Down 81 8.4 Animating the Spin 82 Contents • viii 8.5 The Code So Far 84 8.6 What’s Next 84 9. What’s All That Other Code? ......... 85 Getting Started 859.1 9.2 A Quick Introduction to HTML 85 9.3 Setting the Scene 87 9.4 Using Cameras to Capture the Scene 87 9.5 Using a Renderer to Project What the Camera Sees 88 9.6 Exploring Different Cameras and Renderers 89 9.7 What’s Next 91 10. Project: Collisions ........... 93 Getting Started 9310.1 10.2 Rays and Intersections 94 10.3 The Code So Far 98 10.4 What’s Next 98 11. Project: Fruit Hunt ........... 99 Getting Started 9911.1 11.2 Starting a Scoreboard at Zero 100 11.3 Giving Trees a Little Wiggle 101 11.4 Jumping for Points 103 11.5 Making Our Games Even Better 105 11.6 The Code So Far 107 11.7 What’s Next 107 12. Working with Lights and Materials ....... 109 Getting Started 10912.1 12.2 Changing Color 109 12.3 Realism: Shininess 111 12.4 Shadows 113 12.5 Let’s Animate! 115 12.6 The Code So Far 116 12.7 What’s Next 116 13. Project: Build Your Own Solar System ...... 117 Getting Started 11713.1 13.2 The Sun, Earth, and Mars 117 13.3 Earth-Cam! 121 13.4 The Code So Far 123 13.5 What’s Next 123 Contents • ix 14. Project: Phases of the Moon ........ 125 Getting Started 12614.1 14.2 Change Mars into the Moon 126 14.3 The Coolest Trick: Frame of Reference 127 14.4 Challenge: Create an Earth Orbit Frame of Reference 129 14.5 Pausing the Simulation 129 14.6 Understanding the Phases 131 14.7 The Code So Far 132 14.8 What’s Next 132 15. Project: The Purple Fruit Monster Game ..... 133 Getting Started 13315.1 15.2 Let’s Make Physics! 133 15.3 Outline the Game 135 15.4 The Code So Far 143 15.5 What’s Next 143 16. Project: Tilt-a-Board .......... 145 Getting Started 14616.1 16.2 Gravity and Other Setup 146 16.3 Outline the Game 147 16.4 The Code So Far 157 16.5 What’s Next 157 17. Project: Learning about JavaScript Objects ..... 159 Getting Started 15917.1 17.2 Simple Objects 160 17.3 Copying Objects 161 17.4 Constructing New Objects 162 17.5 The Code So Far 164 17.6 What’s Next 164 18. Project: Cave Puzzle .......... 165 Getting Started 16518.1 18.2 Setting the Game’s Boundaries 167 18.3 Building a Random, Unreachable Goal 170 18.4 Building Draggable Ramps 171 18.5 Winning the Game 174 18.6 The Code So Far 176 18.7 What’s Next 176 Contents • x 19. Project: Multilevel Game ......... 177 Getting Started 17719.1 19.2 Building Levels 178 19.3 Adding Finishing Touches to the Game 183 19.4 The Code So Far 184 19.5 What’s Next 184 20. Project: River Rafting .......... 185 Getting Started 18520.1 20.2 Organizing Code 186 20.3 Warping Shapes to Make Unique Things 189 20.4 Build a Raft for Racing 195 20.5 Setting the Finish Line 198 20.6 The Code So Far 205 20.7 What’s Next 205 21. Getting Code on the Web ......... 207 The Mighty, Mighty Browser 20821.1 21.2 Free Websites 212 21.3 Putting Your Code on Another Site 213 21.4 What’s Next 215 A1. Project Code ............ 217 Code: Creating Simple Shapes 217A1.1 A1.2 Code: Playing with the Console and Finding What’s Broken 218 A1.3 Code: Making an Avatar 219 A1.4 Code: Moving Avatars 220 A1.5 Code: Functions: Use and Use Again 222 A1.6 Code: Moving Hands and Feet 223 A1.7 Code: A Closer Look at JavaScript Fundamentals 226 A1.8 Code: Turning Our Avatar 226 A1.9 Code: What’s All That Other Code? 229 A1.10 Code: Collisions 230 A1.11 Code: Fruit Hunt 234 A1.12 Code: Working with Lights and Materials 240 A1.13 Code: Build Your Own Solar System 241 A1.14 Code: Phases of the Moon 243 A1.15 Code: The Purple Fruit Monster Game 245 A1.16 Code: Tilt-a-Board 249 A1.17 Code: Learning about JavaScript Objects 253 Contents • xi A1.18 Code: Cave Puzzle 255 A1.19 Code: Multilevel Game 259 A1.20 Code: River Rafting 265 A2. JavaScript Libraries Used in This Book ...... 273 Three.js 273A2.1 A2.2 Physijs 273 A2.3 Tween.js 273 A2.4 Scoreboard.js 274 A2.5 Sounds.js 277 Index .............. 279 Contents • xii Acknowledgments I am nothing without my lovely wife, Robin. Not only does she put up with me disappearing for days on end to write, but she also helps in ways innumer- able. She was the primary proofreader for the early versions of the book. She helps to run the kid hackathons (OK, she runs them) that aided in develop- ment of this book. And oh, yeah—she’s an awesome wife and mother. Also a big thanks to my son Luke for being the primary guinea pig for the early versions of the book. His no-nonsense feedback made this a better product. Thanks also to my daughter Elora for chiming in with her insights. And, of course, huge thanks to my technical reviewers. It is a tough task to review a book from a kid’s perspective, but my reviewers were more than up to the task. In no particular order, they are Alec M., Hana B., Dave S., Thad K., Maik Schmidt, Silvia Domenech, and Mark Musante. Special thanks to Sophie H., who provided the inspiration for the game that eventually became Project: Fruit Hunt. This book would not exist without the great work of Ricardo Cabello Miguel, affectionately known as “Mr.doob.” Ricardo is the primary programmer behind Three.js, the 3D JavaScript library that we use in this book. He also wrote the original implementation of the ICE Code Editor that we use. This book would be significantly less without his amazing talents. Thanks also to Chandler Prall for his work on the Physijs physics engine, of which we make extensive use. Chandler was also wonderful about answering my many, many questions while I was learning. Last, but not least, many thanks to the folks at The Pragmatic Programmers for believing in the book and helping me realize its full potential. Special thanks to my editor, Fahmida, for keeping me honest and focused. report erratum • discuss Introduction Welcome to the world of programming! I won’t lie; it can be a frustrating world sometimes (it makes me cry at least once a week). But it’s totally worth the pain. You get to make this world do whatever you want. You can share your world with others. You can build things that really make a difference. This book that you hold in your eager hands is a great way to get started programming. It is chock-full of clear and understandable explanations. Best of all, we get to make some pretty cool games. This is going to be a blast. How I Learned to Program When I was a kid, I copied computer-program games out of books. This was a long time ago, so I bought books with nothing but programs, and typed them into computers. When I first started doing it, I had no idea what I was doing. Eventually, I started to recognize certain things that were done over and over, and I almost understood them. I started to change things—little things at first—to see what happened. Then I started making bigger changes. Eventually I got pretty good at it. And after a long time, I could write my own programs. I hope that this book will let you do the same, but with one important difference: I’ll explain what’s going on so you won’t have to guess quite as much. What You Need for This Book Not all web browsers can generate the cool 3D-gaming objects that we’ll build in this book. To get the most out of the book, you should install the Google Chrome (https://www.google.com/chrome/) web browser on your computer. Other web browsers will work, but some of the exercises in this book rely on features available only in Google Chrome. One browser that will definitely not work with the exercises is Microsoft Internet Explorer. report erratum • discuss For most of the exercises in the book, any computer with Google Chrome installed will be sufficient. Later exercises that make use of interesting lighting, shadows, and 3D materials will require a computer that supports WebGL. You can test your computer’s capabilities by visiting the Get WebGL site (http://get.webgl.org/). Don’t worry much about WebGL; you’ll be able to do a ton of programming even if your computer can’t handle the advanced 3D graphics. What Is JavaScript? There are many, many programming languages. Some programmers enjoy arguing over which is the best, but the truth is that all languages offer unique and worthwhile things. In this book we’ll use the JavaScript programming language. We program in JavaScript because it’s the language of the Web. It is the only programming language all web browsers understand without needing any additional soft- ware. If you can program in JavaScript, not only can you make the kinds of games that you’ll learn in this book, but you can also program just about every website there is. We’re not going to become experts in JavaScript. We’ll cover just enough JavaScript to be able to program the games in this book. That is quite a lot of JavaScript—enough that you’ll be able to learn the rest without much difficulty. How to Read This Book You’ll see two kinds of chapters: project chapters and learning chapters. The project chapters start with “Project” just like Chapter 1, Project: Creating Simple Shapes, on page 1. All the others are learning chapters. If you want to learn programming the way I did, just read the project chapters and follow along with all the exercises. You’ll create pretty cool game charac- ters and worlds to play in. You’ll make space simulations. You’ll make purple monsters. You’ll make all sorts of great stuff. If you have questions about why the games are written the way they are, then read the learning chapters. We won’t go over everything about programming, but there should be enough to help you understand why we do what we do. These are the chapters that I wish I’d had when I was a kid. Let’s Get Started! Enough introduction—let’s jump right into programming! Introduction • xvi report erratum • discuss CHAPTER 1 Project: Creating Simple Shapes There will be plenty of time for detailed explanations later in this book. For now, let’s get started with programming! 1.1 Programming with the ICE Code Editor In this book, we’ll use the ICE Code Editor to do our programming. The ICE Code Editor runs right inside a browser. It lets us type in our programming code and see the results immediately. To get started, open the ICE Code Editor at http://gamingJS.com/ice using Google’s Chrome web browser. It should look something like this: When you’re done with this chapter, you will • Know what a code editor is and how to use it to program • Know how to make various 3D shapes • Be able to program simple JavaScript • Understand how to make 3D shapes move report erratum • discuss That spinning, multisided thing is a sample of some of the stuff we’ll be working on in this book. In this chapter we’ll create a new project named Shapes. To create a new project in the ICE Code Editor, we click on the menu button (the button with three horizontal lines) in the upper-right corner of the screen and select New from the drop-down. Type the name of the project, Shapes, in the text field and click the Save button. Leave the template set as 3D starter project. Remember, none of the projects in this book will work if you’re using the ICE Code Editor in Internet Explorer. Although some of the exercises will work with Mozilla Firefox, it’s easiest to stick with a single browser (Google Chrome) for all our projects. Coding with the ICE Code Editor We’ll be using the ICE Code Editor throughout this book. You only need web access the first time that you connect to http://gamingJS.com/ ice/. After the first visit, ICE is stored in your browser so you can keep working even if you’re not connected to the Internet. Chapter 1. Project: Creating Simple Shapes • 2 report erratum • discuss When ICE opens a new 3D project, there is already a lot of code in the file. We’ll look closely at that code later, but for now let’s begin our programming adventure on line 20. Look for the line that says START CODING ON THE NEXT LINE. On line 20, type the following: var shape = new THREE.SphereGeometry(100); var cover = new THREE.MeshNormalMaterial(); var ball = new THREE.Mesh(shape, cover); scene.add(ball); Once you finish typing that, you should see something cool: The ball that we typed—the ball that we programmed—showed up in ICE. Congratulations! You just wrote your first JavaScript program! Don’t worry about the structure of the code just yet; you’ll get familiar with it in A Closer Look at JavaScript Fundamentals. For now, let’s examine the 3D programming that we just did. report erratum • discuss Programming with the ICE Code Editor • 3 3D things are built from two parts: the shape and something that covers the shape. The combination of these two things, the shape and its cover, is given a special name in 3D programming: mesh. Mesh is a fancy word for a 3D thing. Meshes need shapes (sometimes called geometry) and something to cover them (sometimes called materials). In this chapter we’ll look at different shapes. We won’t deal with different covers for our shapes until Working with Lights and Materials. Once we have a mesh, we add it to the scene. The scene is where the magic happens in 3D programming. It is the world in which everything takes place. In this case, it’s where our ball is hanging out, waiting for some friends. Let’s add some other shapes to the scene so that the ball isn’t lonely. Your Work Is Saved Automatically Your work is saved automatically, so you don’t have to do it yourself. If you want to save the code yourself anyway, click the three-line menu button in ICE and select the Save option from the drop-down. That’s it! 1.2 Making Shapes with JavaScript So far we have seen only one kind of shape: a sphere. Shapes can be simple, like cubes, pyramids, cones, and spheres. Shapes can also be more complex, like faces or cars. In this book we’ll stick with simple shapes. When we build things like trees, we’ll combine simple shapes, such as spheres and cylinders, to make them. Creating Spheres Balls are always called spheres in geometry and in 3D programming. There are two ways to control the shape of a sphere in JavaScript. Size: SphereGeometry(100) The first way that we can control a sphere is to describe how big it is. We created a ball whose radius was 100 when we said new THREE.SphereGeometry(100). What happens when you change the radius to 250? var shape = new THREE.SphereGeometry(250);❶ var cover = new THREE.MeshNormalMaterial(); var ball = new THREE.Mesh(shape, cover); scene.add(ball); ❶ This points to where you should change the sphere’s size. Chapter 1. Project: Creating Simple Shapes • 4 report erratum • discuss This should make it much bigger: What happens if you change the 250 to 10? As you probably guessed, it gets much smaller. So that’s one way we can control a sphere’s shape. What is the other way? Not Chunky: SphereGeometry(100, 20, 15) If you click on the Hide Code button in ICE, you may notice that our sphere isn’t really a smooth ball: You Can Easily Hide or Show the Code If you click the white Hide Code button in the upper-right corner of the ICE window, you’ll see just the game area and the objects in the game. This is how you’ll play games in later chapters. To get your code back, click the white Show Code button within the ICE Code Editor. Computers can’t really make a ball. Instead they fake it by joining a bunch of squares (and sometimes triangles) to make something that looks like a ball. Normally, we’ll get the right number of chunks so that it’s close enough. Sometimes we want it to look a little smoother. To make it smoother, add some extra numbers to the SphereGeometry() line: report erratum • discuss Making Shapes with JavaScript • 5 var shape = new THREE.SphereGeometry(100, 20, 15);❶ var cover = new THREE.MeshNormalMaterial(); var ball = new THREE.Mesh(shape, cover); scene.add(ball); ❶ The first number is the size, the second number is the number of chunks around the sphere, and the third number is the number of chunks up and down the sphere. This should make a sphere that is much smoother: Play around with the numbers a bit more. You’re already learning quite a bit here, and playing with the numbers is a great way to keep learning! Don’t Change the Chunkiness Unless You Have To The number of chunks that we get without telling SphereGeometry to use more may not seem great, but don’t change it unless you must. The more chunks that are in a shape, the harder the computer has to work to draw it. As you’ll see in later chapters, it’s usually easier for a computer to make things look smooth by choosing a different cover for the shape. When you’re done playing, move the ball out of the way by setting its position: var shape = new THREE.SphereGeometry(100); var cover = new THREE.MeshNormalMaterial(); var ball = new THREE.Mesh(shape, cover); scene.add(ball); ball.position.set(-250,250,-250);❶ ❶ The three numbers move the ball to the left, up, and back. Don’t worry much about what the numbers do right now—we’ll talk about position when we start building game characters in Chapter 3, Project: Making an Avatar, on page 25. Making Boxes with the Cube Shape Next we’ll make a cube, which is another name for a box. There are three ways to change a cube’s shape: the width, the height, and the depth. Chapter 1. Project: Creating Simple Shapes • 6 report erratum • discuss Size: CubeGeometry(300, 100, 20) To create a box, we’ll write more JavaScript below everything that we used to create our ball. Type the following: var shape = new THREE.CubeGeometry(100, 100, 100); var cover = new THREE.MeshNormalMaterial(); var box = new THREE.Mesh(shape, cover); scene.add(box); If you have everything correct, you should see…a square: Well, that’s boring. Why do we see a square instead of a box? The answer is that our camera, our perspective, is looking directly at one side of the box. If we want to see more of the box, we need to move the camera or turn the box. Let’s turn the box by rotating it: var shape = new THREE.CubeGeometry(100, 100, 100); var cover = new THREE.MeshNormalMaterial(); var box = new THREE.Mesh(shape, cover); scene.add(box); box.rotation.set(0.5, 0.5, 0);❶ ❶ These three numbers turn the box down, counterclockwise, and left-right. In this case, we rotate 0.5 down and 0.5 to the right: Try This Yourself Rotating things takes a little getting used to, so play with the numbers. Try smaller and bigger numbers. A full rotation is 6.3 (we’ll talk about that number later). Try setting two of the numbers to 0 and another to 0.1, then to 0.25, and finally to 0.5. If you can change the numbers fast enough, it’s almost like the cube is spinning! report erratum • discuss Making Shapes with JavaScript • 7 Setting the box rotation to (0.5, 0.5, 0) should rotate the cube so we can see that it really is a cube: Each side of a cube doesn’t have to be the same size. Our box so far is 100 wide (from left to right), 100 tall (up and down), and 100 deep (front to back). Let’s change it so that it is 300 wide, 100 tall, and only 20 deep: var shape = new THREE.CubeGeometry(300, 100, 20); var cover = new THREE.MeshNormalMaterial(); var box = new THREE.Mesh(shape, cover); scene.add(box); box.rotation.set(0.5, 0.5, 0); This should show something like this: Play around with the numbers to get a good feel for what they can do. Believe it or not, you already know a ton about JavaScript and 3D program- ming. There is still a lot to learn, of course, but you can already make balls and boxes. You can already move them and turn them. And you only had to write ten lines of JavaScript to do it all—nice! Let’s move our box out of the way so we can play with more shapes: var shape = new THREE.CubeGeometry(300, 100, 20); var cover = new THREE.MeshNormalMaterial(); var box = new THREE.Mesh(shape, cover); scene.add(box); box.rotation.set(0.5, 0.5, 0); box.position.set(250, 250, -250); Chapter 1. Project: Creating Simple Shapes • 8 report erratum • discuss Using Cylinders for All Kinds of Shapes A cylinder, which is also sometimes called a tube, is a surprisingly useful shape in 3D programming. Think about it: cylinders can be used as tree trunks, tin cans, wheels…. Did you know that cylinders can be used to create cones, evergreen trees, and even pyramids? Let’s see how! Size: CylinderGeometry(20, 20, 100) Below the box code, type in the following to create a cylinder: var shape = new THREE.CylinderGeometry(20, 20, 100); var cover = new THREE.MeshNormalMaterial(); var tube = new THREE.Mesh(shape, cover); scene.add(tube); If you rotate that a little (you remember how to do that from the last section, right?), then you might see something like this: If you were not able to figure out how to rotate the tube, don’t worry. Just add this line after the line with scene.add(tube): tube.rotation.set(0.5, 0, 0); When making a cylinder, the first two numbers describe how big the top and bottom of the cylinder is. The last number is how tall the cylinder is. So our cylinder has a top and bottom that are 20 in size and 100 in height. If you change the first two numbers to 100 and the last number to 20, what happens? What happens if you make the top 1, the bottom 100, and the height 100? Try This Yourself Play with those numbers and see what you can create! What did you find? A flat cylinder is a disc: report erratum • discuss Making Shapes with JavaScript • 9 And a cylinder that has either the top or bottom with a size of 1 is a cone: It should be clear that you can do a lot with cylinders, but we haven’t seen everything yet. We have one trick left. Pyramids: CylinderGeometry(1, 100, 100, 4) Did you notice that cylinders look chunky? It should be no surprise then, that you can control the chunkiness of cylinders. If you set the number of chunks to 20, for instance, with the disc, like this: var shape = new THREE.CylinderGeometry(100, 100, 10, 20); var cover = new THREE.MeshNormalMaterial(); var tube = new THREE.Mesh(shape, cover); scene.add(tube); tube.rotation.set(0.5, 0, 0); then you should see something like this: Just as with spheres, you should use lots of chunks like that only when you really, really need to. Can you think how you might turn this into a pyramid? You have all of the clues that you need. Chapter 1. Project: Creating Simple Shapes • 10 report erratum • discuss Try This Yourself Play with different numbers and see what you can create! Were you able to figure it out? Don’t worry if you weren’t. The way we’ll do it is actually pretty sneaky. The answer is that you need to decrease the number of chunks that you use to make a cone. If you set the top to 1, the bottom to 100, the height to 100, and the number of chunks to 4, then you’ll get this: It might seem like a cheat to do something like this to create a pyramid, but this brings us to a very important tip with any programming: Cheat Whenever Possible You shouldn’t cheat in real life, but in programming—especially in 3D programming—you should always look for easier ways of doing things. Even if there is a usual way to do something, there may be a better way to do it. You’re doing great so far. Move the tube out of the center like we did with the cube and the sphere: tube.position.set(250, -250, -250); Now let’s move on to the last two shapes of this chapter. Building Flat Surfaces with Planes A plane is a flat surface. Planes are especially useful for the ground, but they can also be handy to mark doors and edges in our games. PlaneGeometry(100, 100) Since planes are just flat squares, they are much simpler than the other objects that we’ve seen. Type in the following: report erratum • discuss Making Shapes with JavaScript • 11 var shape = new THREE.PlaneGeometry(100, 100); var cover = new THREE.MeshNormalMaterial(); var ground = new THREE.Mesh(shape, cover); scene.add(ground); ground.rotation.set(0.5, 0, 0); Don’t forget the rotation on the last line. Planes are so thin that you might not see them when looking directly at them. The numbers when building a plane are the width and depth. A plane that is 300 wide and 100 deep might look like this: That’s pretty much all there is to know about planes. Move our plane out of the way: var shape = new THREE.PlaneGeometry(300, 100); var cover = new THREE.MeshNormalMaterial(); var ground = new THREE.Mesh(shape, cover); scene.add(ground); ground.position.set(-250, -250, -250); Now let’s move on to the greatest shape in the world. Rendering Donuts (Not the Kind You Eat) with Torus In 3D-programming-speak, a donut is called a torus. The simplest torus that we can create needs us to assign two values: one for the distance from the center to the outside edge, and the other for the thickness of the tube. TorusGeometry(100, 25) Type the following into ICE: var shape = new THREE.TorusGeometry(100, 25); var cover = new THREE.MeshNormalMaterial(); var donut = new THREE.Mesh(shape, cover); scene.add(donut); You should see a very chunky donut, as shown in Figure 1, A Chunky Donut, on page 13. By now you probably know how to make the donut less chunky. Chapter 1. Project: Creating Simple Shapes • 12 report erratum • discuss Figure 1—A Chunky Donut TorusGeometry(100, 25, 8, 25) Like the sphere, the donut shape is built from chunks. The chunks can be made bigger or smaller around the inner tube, which we can set by including a third number when defining the TorusGeometry. We can also adjust the size of the chunks around the outside of the donut by including a fourth number. Try experimenting with numbers like the following and see what happens. var shape = new THREE.TorusGeometry(100, 25, 8, 25); var cover = new THREE.MeshNormalMaterial(); var donut = new THREE.Mesh(shape, cover); scene.add(donut); Now that is a good-looking donut: TorusGeometry(100, 25, 8, 25, 3.14) We can play one other trick with donuts. Try adding another number, 3.14, to the TorusGeometry shape: report erratum • discuss Making Shapes with JavaScript • 13 var shape = new THREE.TorusGeometry(100, 25, 8, 25, 3.14); var cover = new THREE.MeshNormalMaterial(); var donut = new THREE.Mesh(shape, cover); scene.add(donut); That should make a half-eaten donut. 1.3 Animating the Shapes Before we finish our first programming session, let’s do something cool. Let’s make all of our shapes spin around like crazy. In ICE, add the following code after all of the shapes: var clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); var t = clock.getElapsedTime(); ball.rotation.set(t, 2*t, 0); box.rotation.set(t, 2*t, 0); tube.rotation.set(t, 2*t, 0); ground.rotation.set(t, 2*t, 0); donut.rotation.set(t, 2*t, 0); renderer.render(scene, camera); } animate(); Don’t worry about what everything means in that code—we’ll look at all of these lines in greater detail later in the book. For now, it’s enough to know that at specific time intervals, we’re changing the shape’s rotation. After each change, we tell the rendering program to redraw the current shapes on the screen. If ICE Locks Up When doing animations and other sophisticated programming, it’s possible to completely lock up the ICE Code Editor. This is not a big deal. If ICE stops responding, you’ll need to undo whatever change you made last. Instructions on how to do that are in Recovering When ICE Is Broken. Chapter 1. Project: Creating Simple Shapes • 14 report erratum • discuss 1.4 The Code So Far To make things a little easier, the completed version of this project is included as part of Section A1.1, Code: Creating Simple Shapes, on page 217. Use that code to double-check your work as you go through the exercises, but do not copy and paste code into ICE. It’s impossible to learn and understand pro- gramming unless you code it yourself. 1.5 What’s Next Whoa! That was pretty crazy. We learned a ton and we’re just getting started! Already we know how to code projects in the ICE Code Editor. We know how to make a lot of different shapes. We even know how to move and spin things with JavaScript. And best of all, it took us only fifteen lines of code to create a pretty cool animation after making our shapes. That’s a good start. Now that we have a taste of how to do 3D programming, let’s talk a little bit about programming in web browsers. report erratum • discuss The Code So Far • 15 CHAPTER 2 Playing with the Console and Finding What’s Broken When programming within web browsers, it’s extremely useful to be able to use the browser’s JavaScript console. Most modern browsers have a JavaScript console, but here we’re using Google Chrome. Programming Can Be Overwhelming At times it can make you want to throw your computer against the wall (don’t). While doing this stuff, keep these two facts in mind: • There will be things that you don’t know—this is OK. • Your programs are going to break—this is OK. Just remember that everyone struggles with this, and you’ll be just fine. 2.1 Getting Started Know the ICE Code Editor We’re still using the ICE Code Editor that we used in Chapter 1, Project: Creating Simple Shapes, on page 1. If you haven’t already gotten started with ICE, go back to that chapter and familiarize yourself with the editor. Start a New Project Any work that you have already done in ICE should be automatically saved, so we can jump right into starting a new project. Click on the menu button and then choose New from the menu: When you’re done with this chapter, you will • Be able to open/close the JavaScript console • Know how to look for errors in the JavaScript console • Be able to fix projects when ICE locks up report erratum • discuss Let’s call the new project Breaking Things. Be sure to leave the template set to 3D starter project. Now let’s open the browser’s JavaScript console. 2.2 Opening and Closing the JavaScript Console The JavaScript console inside the browser is a web programmer’s best friend. It tells us where we made our mistakes. Opening and Closing the JavaScript Console Ctrl+Shift+J (holding down the Ctrl, Shift, and J keys at the same time) will open and close the JavaScript console. If you’re using an Apple computer, you can use D+Option+J to open and close the console. Don’t worry if you see tons of warnings and errors the first time you open the JavaScript console. It keeps a log of events that hap- pen on a web page or in the ICE Code Editor. If the messages are too much, you can clear the log with the button that has a circle with a line through it. The same key combination that opens the JavaScript console will close it (but leave it open in this chapter). Chapter 2. Playing with the Console and Finding What’s Broken • 18 report erratum • discuss Let’s start by breaking simple things that the ICE Code Editor can tell us about. 2.3 Debugging in ICE: The Red X A red X next to your code means ICE sees a problem that will stop your code from running. Let’s write some really bad JavaScript to demonstrate this. Enter the following line below START CODING ON THE NEXT LINE. bad()javascript That’s some bad JavaScript! Are you wondering why? It’s bad because you should never have a word come after the parentheses. If you write code like this, ICE will show a red X next to the line with the problem to indicate that line has to be fixed. Moving the mouse pointer over the red X will display the actual error message, such as “missing ; before statement.” ICE won’t tell you that you put words after the parentheses. All it knows is that parentheses should be at the end of the line, and here that wasn’t the case. When it detected the line wasn’t finished, it triggered an error to warn, “Hey! You forgot the semicolon at the end of the line!” It’s up to us to figure out where the line should end. Some things to check in your code when you see a red X: • Did you forget a semicolon? • If you don’t see a problem on the red X line, look at the previous line, as well. ICE can’t always tell where the problem begins and may be off by one or two lines. 2.4 Debugging in ICE: The Yellow Triangle Unlike a red X, a yellow triangle showing up to the left of your code is not a show-stopper. Your code will probably run even if lines in your code are marked with yellow triangles, but it may not run correctly. It is best to get rid of those triangles as they come up. report erratum • discuss Debugging in ICE: The Red X • 19 Let’s put this in action by writing some more bad JavaScript (but not too bad). First, remove the bad()javascript line from the previous section, and add the fol- lowing lines: food; eat(food); In this case, ICE will tell us via the yellow triangle that the food line is not doing anything. We can fix the problem by changing the food line into an assignment, like this: food = 'Cookie'; eat(food); ICE should accept the new food line and no longer display any errors. However, even though ICE may not report any more issues, there is still something wrong with this code. 2.5 Debugging in the Console This is where the JavaScript console comes in handy, as we get to see what the program is actually doing. Once you open up the console, you’ll see an error message that eat() is not defined. When the browser tried to run the bad JavaScript code, it realized there was a problem. In our program, we told the browser to run the eat() function, but we never told the browser how to do that. Errors found when trying to run the code are called run-time errors. We’ll talk more about functions in Chapter 5, Functions: Use and Use Again, on page 49. For now, it’s enough to know that a function is a way to write code that can be run again and again. The errors flagged by ICE with the red X and yellow triangle are called compile- time errors. Compile-time errors are caught when the computer is reading the code and deciding what to do with it. Compiling refers to the computer deciding what to do with the code. The JavaScript console helps us fix run-time errors. Chapter 2. Playing with the Console and Finding What’s Broken • 20 report erratum • discuss To resolve this problem, let’s tell our JavaScript program how to eat food. We do this by adding a function that explains eating after the line with eat(food). food = 'Cookie'; eat(food); function eat(food) { console.log(food + "! Nom. Nom. Nom."); } At this point, there should be no compile-time errors in ICE, no run-time errors in the JavaScript console, and the message, “Cookie! Nom. Nom. Nom.” in the console. Before we wrap up this chapter, let’s look at some common 3D-programming errors. Add the following code after the closing curly brace of the eat() function: var shape = new THREE.SpherGeometry(100); var cover = new Three.MeshNormalMaterial(); var ball = new THREE.Mesh(shape, cover); scene.ad(ball); You’ll notice that there are no compile-time errors in ICE for this code. The browser reads the JavaScript code and says, “Yup, that looks like perfectly fine JavaScript to me. I’ll run it now!” However, problems pop up when the code is actually run, and you’ll see run-time errors in the JavaScript console. Possible Error Message—Undefined Is Not a Function Let’s take a look at what went wrong. First, open the JavaScript console if it’s not already open. In there, you should see a very unhelpful message. This message is trying to tell us that SphereGeometry is spelled incorrectly. Check the code; it turns out we missed an e and typed SpherGeometry instead. The message in the JavaScript console is very poor and unhelpful. There are two problems to tackle here. First, “undefined is not a function” doesn’t really tell us anything and is not easy to understand. Even JavaScript experts get confused by that one. The second problem is the line number in the error message. In this example, gamingjs.com:25 means the browser thinks the problem happened on line 25 of report erratum • discuss Debugging in the Console • 21 our program (your line numbers may be slightly different). However, the misspelled word is not on line 25 in ICE. Our problem actually happens on line 28. And yes, JavaScript experts get confused by this as well. Console Line Numbers Are Not Always Exact ICE does its best to get the line numbers in the console correct, and sometimes it succeeds—it may even be correct for you now— but other times it can be off by a few lines. Start by looking at the exact line number. If that doesn’t seem like it matches the error, then check the next few lines. Let’s get back to the “undefined is not a function” error message that is actually referring to line 28 in ICE. This error means that when the browser tried to run our code, it was looking for a function but found something it knew nothing about. THREE.SpherGeometry was not defined because the actual function was called THREE.SphereGeometry. Luckily it’s easy to fix this problem, as all we have to do is add the e. Possible Error Message—Three Is Not Defined However, even after we spell SphereGeometry correctly, a ball doesn’t appear on the screen. Something is still wrong with our code. Looking in the JavaScript console, you should see something like the following. Here, the JavaScript console is telling us that we forgot THREE should always be all capital letters. There is no such thing as Three, which is what we wrote and what the JavaScript console is telling us. This is a very common mistake when working with the 3D library, so make sure you remember it for the next time you see the error. We can fix this problem by replacing the Three in the code with THREE. Possible Error Message—Undefined: No Method Even with those two issues fixed, the sphere is still not visible and we have another cryptic error message in the console. Chapter 2. Playing with the Console and Finding What’s Broken • 22 report erratum • discuss Don’t worry about the Object [object Object] part of the message, as it’s not telling us anything helpful at this point. In this case, we told the browser that there was a method named ad(), but it was unable to find any information in the file. The fix is the same as in previ- ous examples. The method we should have is add(), not ad(). In other words, we do not want to ad the ball to the screen; we want to add it. After fixing that line, you’ll finally see a ball and the “Nom. Nom. Nom.” mes- sage appear in the Javascript console. 2.6 Recovering When ICE Is Broken It is surprisingly easy to break a web browser. If you create a sphere with a million chunks, the browser will break. If you create a recursive function with no stopping point (we’ll talk about those in Functions: Use and Use Again), the browser will break. If the browser is broken, then the ICE Code Editor is broken, right? Well, yes, but there’s an easy way to fix it: add ?e or ?edit-only to the URL so that you’re looking at http://gamingjs.com/ice/?e. This is edit-only mode for ICE. report erratum • discuss Recovering When ICE Is Broken • 23 Fix the last thing that you typed to break ICE, and then remove the edit-only question mark from the URL so that you’re back at http://gamingjs.com/ice/. Now you should see the preview again. On some computers, you may find that you need to close the browser tab or window before trying this. Then you can open a new window or tab in which you can enter the ICE edit-only URL. Google Chromebooks, in particular, run edit-only mode better with this procedure. 2.7 What’s Next Now that we know how to make shapes and where to check when things go wrong, let’s get started on our first game by building our very own avatar. Chapter 2. Playing with the Console and Finding What’s Broken • 24 report erratum • discuss CHAPTER 3 Project: Making an Avatar Developing games involves building a lot of parts, such as the game area, the players in the game, and things that get in the way of players. In this project chapter we’ll create a player that we might use in a game—an avatar. It will end up looking something like this: An avatar is who you are within the game world. It shows where you are in the game and what you’re doing. Since it’s supposed to represent you and me, it should have a good feel to it. We want something better than a plain box to stand for us. The Difference Between a Player and an Avatar In this book, we’ll use the word “player” to mean the person playing the game. The word “avatar” will be used to describe a player inside the game. When you’re done with this chapter, you will • Know how to place objects together on the screen • Have an avatar to use in later chapters and in games report erratum • discuss 3.1 Getting Started Let’s open the ICE Code Editor1 again and create a new project named My Avatar (check Start a New Project, on page 17, if you don’t remember how). Be sure to leave the template set to 3D starter project. With that, we’re ready to start programming on the line following START CODING ON THE NEXT LINE. 3.2 Making a Whole from Parts From Chapter 1, Project: Creating Simple Shapes, on page 1, we already know how to make basic shapes. Let’s start building our player avatar by making a sphere for the body. var cover = new THREE.MeshNormalMaterial(); var body = new THREE.SphereGeometry(100); var avatar = new THREE.Mesh(body, cover); scene.add(avatar); We already know what happens when we type that in—we get a ball in the center of the scene. Let’s add a hand next to the body. Add the following lines below the code that you already entered to create the body. var hand = new THREE.SphereGeometry(50); var right_hand = new THREE.Mesh(hand, cover); right_hand.position.set(-150, 0, 0); scene.add(right_hand); 1. http://gamingJS.com/ice Chapter 3. Project: Making an Avatar • 26 report erratum • discuss Notice that we didn’t create a new cover for the hand. Instead we reused the same cover, which we named cover when we used it for the avatar’s body. That saves us a bit of typing. Less typing is a good thing since we’re all programmers and programmers are lazy at heart. That reminds me of some programming wisdom I would like to share with you: Good Programmers Are Lazy I don’t mean that programmers hate doing work. We actually love our jobs and often spend too much time working because we love it so much. No, what I mean by lazy is that we hate doing work that computers are better at. So instead of creating hands and feet individually, we would rather write a single hand/foot and then copy it as many times as necessary. Being lazy benefits us in two very important ways: • We type less. Believe it or not, this is a big win. Not only do we have to type less the first time around, but we have to read less when we want to update later. • If we want to change the way a limb is created, we only have to change one thing. That is, if we want to change the cover or even the shape of a hand in the future, then we only have to make a change in one place. So let’s see if we can be even lazier when we create the left hand for our avatar: var left_hand = new THREE.Mesh(hand, cover); left_hand.position.set(150, 0, 0); scene.add(left_hand); Not only did we not make a new cover for the left hand, but we also didn’t create a new shape! Instead we just used the same shape for the left hand that we did for the right hand. Now that’s lazy! With that, our avatar should look something like Figure 2, Avatar with Hands, on page 28. OK, I admit that doesn’t look much like a body with hands. Bear with me for a bit longer, and you’ll see. report erratum • discuss Making a Whole from Parts • 27 Figure 2—Avatar with Hands 3.3 Breaking It Down Let’s take a quick look at why we used those numbers for the hands. If you’re impatient, skip ahead to Section 3.4, Adding Feet for Walking, on page 29, to keep building our game avatar. When anything is added to a scene, it starts off in the very center. So when we add the body and a hand, it starts off something like this: In 3D programming and mathematics, left and right are called the X direction. Up and down are called the Y direction. This is why we change the X position of the hands: var left_hand = new THREE.Mesh(hand, cover); left_hand.position.set(150, 0, 0); scene.add(left_hand); The numbers inside left_hand.position.set(150, 0, 0) are the X, Y, and Z position of the left hand (Z would be forward and backward). We set X to 150 while Y and Z are both 0. This is really the same thing as left_hand.position.x = 150. As we’ll see shortly, it can be very convenient to set multiple values on a single line. Chapter 3. Project: Making an Avatar • 28 report erratum • discuss But why 150? The answer is that the radius of the body is 100 and the radius of the hand is 50. We need to move the hand 100 + 50, or 150 in the X (left/right) direction: If we only moved the center of the hand 100, then we would end up with the hand partly inside the body: Try This Yourself If you’re not convinced, try it yourself. Change the number for the X position by fiddling with the first number in right_hand.position.set(- 150, 0, 0). Try it for both the left and right hand. Don’t make them too big, though, or they won’t even be on the screen anymore! 3.4 Adding Feet for Walking For the feet, we’ll again use spheres of size 50. I’ll leave it up to you to figure out how to add the relevant lines. Some hints: • Don’t move the feet left/right as far as we did the hands. The feet should be underneath the body. report erratum • discuss Adding Feet for Walking • 29 • You’ll have to move them down. The up/down positioning is done with the Y direction instead of the X direction. This is the second number of right_hand.position.set(-150, 0, 0). You may have to use negative numbers to go down—for example, –25. • Recall that the hand was added before we rendered the scene—before the line with renderer.render(scene, camera). The feet should be as well. Here is how we did the right hand; it might help while you try to figure out the feet: var hand = new THREE.SphereGeometry(50); var right_hand = new THREE.Mesh(hand, cover); right_hand.position.set(-150, 0, 0); scene.add(right_hand); Good luck! Try This Yourself Try to place the feet yourself. To move the feet left and right, you change the first number in right_foot.position.set(0, 0, 0). To move it up and down, you change the second number (the third number is forward and backward). It may take a while to get it right, but believe me—it’s good practice. Try for a bit and then continue with the text. Did you get it? This is what it might look like: Don’t worry if yours is not exactly the same. Yours may even be better! If you’re having difficulty, refer to the code that was used to make the avatar: Chapter 3. Project: Making an Avatar • 30 report erratum • discuss var cover = new THREE.MeshNormalMaterial(); var body = new THREE.SphereGeometry(100); var avatar = new THREE.Mesh(body, cover); scene.add(avatar); var hand = new THREE.SphereGeometry(50); var right_hand = new THREE.Mesh(hand, cover); right_hand.position.set(-150, 0, 0); scene.add(right_hand); var left_hand = new THREE.Mesh(hand, cover); left_hand.position.set(150, 0, 0); scene.add(left_hand); var foot = new THREE.SphereGeometry(50); var right_foot = new THREE.Mesh(foot, cover); right_foot.position.set(-75, -125, 0); scene.add(right_foot); var left_foot = new THREE.Mesh(foot, cover); left_foot.position.set(75, -125, 0); scene.add(left_foot); This is everything after START CODING ON THE NEXT LINE. 3.5 Challenge: Make the Avatar Your Own If you’re up for a challenge, see if you can create an avatar that looks some- thing like this: To make this, you need to replace the body with one of the shapes from Chapter 1, Project: Creating Simple Shapes, on page 1, and add a head. Don’t report erratum • discuss Challenge: Make the Avatar Your Own • 31 worry about arms and legs to connect the hands and feet to the body—that would make it harder in later chapters. And, of course, you can make whatever kind of avatar you like. Just remember to make one with hands and feet—we’ll need them in later chapters. 3.6 Doing Cartwheels We’ll add controls to our avatar later. But before moving on to the next lesson, let’s make the avatar do some flips and cartwheels. Just like we did at the end of Chapter 1, Project: Creating Simple Shapes, on page 1, we start by changing the very last line of the code (which is just above the ) tag at the end of the editor. Instead of telling the browser to show the scene one time, we animate the scene as follows. // Now, animate what the camera sees on the screen: function animate() { requestAnimationFrame(animate); avatar.rotation.z = avatar.rotation.z + 0.05; renderer.render(scene, camera); } animate(); If you typed everything correctly, you might notice something odd. Just the head is spinning, not the whole avatar. That might be a cool effect, but it’s not what we wanted. So how do we go about spinning the whole avatar? If you guessed that we add rotation.z changes to the hands and feet, you made a good guess. But that won’t work. The hands and feet would spin in place just like the head. The answer to this problem is a very powerful 3D-programming technique. We group all of the body parts together and spin the group. It is a simple idea, but, as you’ll find later, it’s surprisingly powerful. To group the body parts together, we add the parts to the avatar instead of the scene. Chapter 3. Project: Making an Avatar • 32 report erratum • discuss If you look back up to the right hand, you’ll see that we added it to the scene. We’ll change that line. var right_hand = new THREE.Mesh(hand, cover); right_hand.position.set(-150, 0, 0); scene.add(right_hand);❶ ❶ Change this line. Instead of adding the hand to the scene, we add it to the avatar: var right_hand = new THREE.Mesh(hand, cover); right_hand.position.set(-150, 0, 0); avatar.add(right_hand);❶ ❶ This line now adds the right hand to the avatar instead of the scene. After doing the same for the left_hand, the right_foot, and the left_foot, your avatar should be doing cartwheels—without losing any parts! Sometimes we might not want our avatar to do cartwheels. Let’s add a line to control that. var is_cartwheeling = false;❶ function animate() { requestAnimationFrame(animate); if (is_cartwheeling) {❷ avatar.rotation.z = avatar.rotation.z + 0.05; } renderer.render(scene, camera); } animate(); ❶ This is where we say if our avatar is doing cartwheels or not. If we set this to true, then our avatar is doing cartwheels. If we set it to false (like we’ve done here), then our avatar won’t do cartwheels. ❷ Wrap the avatar.rotation in an if, as shown. Don’t forget the curly braces on this line and after the avatar.rotation line. Change the value of is_cartwheeling from false to true. Does the avatar start cartwheeling again? report erratum • discuss Doing Cartwheels • 33 Make Our Avatar Flip! Now that you have the avatar cartwheeling, try to make the avatar flip, as well. You should use a value like is_flipping to control the flipping. Hint: instead of avatar.rotation.z, try avatar.rotation.x or avatar.rotation.y. Did you get it? If not, it’s OK. We’ll cover more of this in later chapters. 3.7 The Code So Far The entirety of the code will look something like the code in Section A1.3, Code: Making an Avatar, on page 219. Don’t worry if yours is not exactly like that code. Your code may be better or just different. 3.8 What’s Next We have a pretty cool-looking avatar. It might be nice for it to have a face or clothes. But you know what would be even better? If we could move our avatar with the keyboard. And that is just what we’ll do in Chapter 4, Project: Moving Avatars, on page 35. For now, take some time to play with the size, positioning, and rotation of the parts that make up your avatar. Chapter 3. Project: Making an Avatar • 34 report erratum • discuss CHAPTER 4 Project: Moving Avatars In Chapter 3, Project: Making an Avatar, on page 25, we covered how to build a game avatar. An avatar that we cannot move is pretty dull. So in this chapter you’ll learn how to make the avatar move in different directions. We’ll also give it a little forest to move around in. It will end up looking something like this: 4.1 Getting Started This chapter builds on the work that we did in Project: Making an Avatar. If you haven’t already done the exercises in that chapter, go back and do them before proceeding. In particular, you need to go over the animate() exercise at the end of that chapter. Let’s make a copy of the previous chapter’s avatar project. That way, if we ever want to go back to see our simple spinning and cartwheeling avatar, we can. To make a copy of that project, click the menu button and choose Make a Copy from the menu. (See Figure 3, Selecting Make a Copy, on page 36. Let’s call this project My Avatar: Keyboard Controls, as shown in Figure 4, Naming the Project, on page 36. With that, we’re ready to add keyboard controls. When you’re done with this chapter, you will • Know how to move the avatar with your keyboard • Begin to understand JavaScript events • Be able to move the camera with an avatar report erratum • discuss Figure 3—Selecting Make a Copy Figure 4—Naming the Project 4.2 Building Interactive Systems with Keyboard Events So far in this book, our code has been very linear—it follows a straight line. First we made a cover, a shape, and a mesh, and then we added the objects to the scene. Then we moved to the next mesh, which was also added to the scene. Although it is possible to write a lot of JavaScript that looks like this, most JavaScript programs tend to be quite different. That’s because JavaScript usually runs in a web browser. In a web browser, JavaScript code has to respond to events. A key being pressed on the keyboard, a mouse button being clicked, and the mouse pointer moving around the page are all events in the web browser. A crazy number of events can poten- tially happen on every web page, and for the most part, these events are ignored. But we’re not going to ignore key presses. We’ll listen for events with something called—you guessed it—an event listener. Let’s add the following at the very bottom of our code, below the animate() line that we added in Chapter 3, Project: Making an Avatar, on page 25. Chapter 4. Project: Moving Avatars • 36 report erratum • discuss // Listen for keypress events document.addEventListener('keydown', function(event) { alert(event.keyCode); }); This adds an event listener to the entire page. It listens for keydown events. When our code notices a keydown, it will alert us with the keycode of the event that just occurred. What is a keycode? To answer that, let’s try it out! Click the Hide Code button in the toolbar at the top of the page, then press the A key on your keyboard. You should see something like this alert dialog. What is this 65? Keep in mind that computers store everything, even letters, as numbers. The computer converts that number into a letter when displaying the correct letter to us humans. When we think of the letter a, a computer is thinking 65. Why do we need to know this? Click the OK button on the alert if you haven’t already done so. Then repeat for the left, up, right, and down arrow keys on your keyboard. For the left arrow, you should discover that the computer thinks 37. For the up arrow, the computer thinks 38. For the right arrow, the computer detects the key as 39. For the down arrow, the computer thinks 40. Let’s use those keycodes to move our avatar! 4.3 Converting Keyboard Events into Avatar Movement By playing with the keyboard event listener, we now know the numbers that correspond to each of the four arrow keys. We convert those arrow keys and numbers into avatar movement like this: Avatar DirectionComputer NumberArrow Key Left37Left Forward38Up Right39Right Back40Down report erratum • discuss Converting Keyboard Events into Avatar Movement • 37 So let’s make this happen. Remove the alert(event.keyCode) line inside the docu- ment.addEventListener(). Replace it with the following code, starting with the var code statement. document.addEventListener('keydown', function(event) { var code = event.keyCode; if (code == 37) avatar.position.x = avatar.position.x-5; // left if (code == 38) avatar.position.z = avatar.position.z-5; // up if (code == 39) avatar.position.x = avatar.position.x+5; // right if (code == 40) avatar.position.z = avatar.position.z+5; // down }); We saw the if statement in Project: Making an Avatar. In this case, we’re checking if the keycode is equal to one of the arrow-key computer numbers. If the key code is 37 (left arrow key), then we change the avatar’s X position by subtracting 5. A double equals (==) in JavaScript checks if something is equal to something else—a single equal (=) makes a value equal to something else. In our preceding code example, we make code equal to event.keyCode. Then we check to see if it is equal to the different arrow-key values. Give It a Try! Press the Hide Code button and give it a try. Use the arrow keys to move the avatar around. Does it work like you expect? Remember: If something goes wrong, check the JavaScript console! If everything is working correctly, then you should be able to move your avatar far away, up close, all the way to the left or right, and even off the screen. You learned how to make sure the avatar’s hands and feet move with the body when we added the ability to do cartwheels back in Section 3.6, Doing Cartwheels, on page 32. Since the hands and feet were added to the avatar object instead of the scene, moving the avatar means that the hands and feet go along with it. Chapter 4. Project: Moving Avatars • 38 report erratum • discuss Let’s see what happens if one of the legs is not attached to the avatar. In this case, we’ll change the left_foot so that it’s added to the scene instead of the avatar. var left_foot = new THREE.Mesh(foot, cover); left_foot.position.set(75, -125, 0); scene.add(left_foot); Run this, and the left foot goes missing. Don’t underestimate the power of this concept. We’ll do some crazy things with it later. For now, don’t forget to fix your left foot to the avatar! 4.4 Challenge: Start/Stop Animation Remember the is_cartwheeling and is_flipping values from when we built the avatar in Chapter 3, Project: Making an Avatar, on page 25? Let’s add two more if statements to the keyboard event listener. If the C key, which the computer thinks is the number 67, is pressed, then the avatar should either start or stop cartwheeling. If the F key, which the computer thinks is 70, is pressed, then the flip routine should start or stop. Hint: switch between true and false with the not operator. In JavaScript, the not operator is an exclamation point, !. You can use this not operator to assign the value of is_cartwheeling to the opposite of its original value with something like is_cartwheeling = !is_cartwheeling. We’ll see this again in Booleans. Hopefully, you were able to get it working yourself. Here is the animate() function that handles the cartwheeling and flipping. var is_cartwheeling = false; var is_flipping = false; function animate() { requestAnimationFrame(animate); if (is_cartwheeling) { avatar.rotation.z = avatar.rotation.z + 0.05; } report erratum • discuss Challenge: Start/Stop Animation • 39 if (is_flipping) { avatar.rotation.x = avatar.rotation.x + 0.05; } renderer.render(scene, camera); } animate(); Here is the complete keyboard event listener for moving, flipping, and cartwheeling our avatar. document.addEventListener('keydown', function(event) { var code = event.keyCode; if (code == 37) avatar.position.x = avatar.position.x-5; // left if (code == 38) avatar.position.z = avatar.position.z-5; // up if (code == 39) avatar.position.x = avatar.position.x+5; // right if (code == 40) avatar.position.z = avatar.position.z+5; // down if (code == 67) is_cartwheeling = !is_cartwheeling; // C if (code == 70) is_flipping = !is_flipping; // F }); If you’ve got it right, you should be able to make the avatar do flips and cartwheels as it moves off the screen. Actually, it’s pretty crazy that the avatar can leave the screen. We’ll fix that in a bit, but first let’s add some trees for our avatar to walk through. 4.5 Building a Forest with Functions We’ll need a lot of trees for our forest. We could build them one at a time, but we’re not going to do that. Instead, let’s add the following JavaScript after all of the avatar body parts: makeTreeAt( 500, 0); makeTreeAt(-500, 0); makeTreeAt( 750, -1000); makeTreeAt(-750, -1000); Chapter 4. Project: Moving Avatars • 40 report erratum • discuss function makeTreeAt(x, z) { var trunk = new THREE.Mesh( new THREE.CylinderGeometry(50, 50, 200), new THREE.MeshBasicMaterial({color: 0xA0522D}) ); var top = new THREE.Mesh( new THREE.SphereGeometry(150), new THREE.MeshBasicMaterial({color: 0x228B22}) ); top.position.y = 175; trunk.add(top); trunk.position.set(x, -75, z); scene.add(trunk); } If you entered all that code correctly, you’ll see the avatar standing in front of a forest of four trees. That’s pretty cool, but how did we do that? Breaking It Down The first part of the forest-building is pretty easy to follow. We add trees at different X and Z coordinates (remember that Y is up and down) around the scene. makeTreeAt( 500, 0); makeTreeAt(-500, 0); makeTreeAt( 750, -1000); makeTreeAt(-750, -1000); That’s easy enough, but how does that makeTreeAt() thing work? As we’ll see in Chapter 5, Functions: Use and Use Again, on page 49, a Java- Script function is a way to run the same code over and over. In this case, we report erratum • discuss Building a Forest with Functions • 41 do all of the repetitive work of building the trunk and treetop in the function named makeTreeAt(). We could have named it anything, but we give it a name that tells us what it does—in this case, it makes a tree at the coordinates that we defined. We should be familiar with most of the things going on inside this function. function makeTreeAt(x, z) { var trunk = new THREE.Mesh(❶ new THREE.CylinderGeometry(50, 50, 200), new THREE.MeshBasicMaterial({color: 0xA0522D}) ); var top = new THREE.Mesh(❷ new THREE.SphereGeometry(150), new THREE.MeshBasicMaterial({color: 0x228B22}) ); top.position.y = 175;❸ trunk.add(top);❹ trunk.position.set(x, -75, z);❺ scene.add(trunk);❻ } ❶ Make a trunk out of a cylinder. ❷ Make a treetop out of a sphere. ❸ Move the treetop up (remember Y is up and down) to the top of the trunk. ❹ Add the treetop to the trunk. ❺ Set the position of the trunk to the x and z that the function was called with—makeTreeAt(500,0)). The Y value of -75 moves the trunk down enough to look like a tree trunk. ❻ Add the trunk to the scene. It’s important to remember that we have to add the treetop to the trunk and not the scene. If we added the treetop to the scene, then when we try to move the tree, only the trunk will be moved and not the treetop. We would also have to set the treetop position if we added it to the scene—adding it to the trunk means that the treetop’s position is the same as the trunk’s. By now we’re getting good at building objects from shapes and materials and placing them on the screen. You could probably make four trees without too much effort. For one tree you need a THREE.CylinderGeometry for the trunk and a THREE.SphereGeometry for the top of the tree. If you add the green leaves to the top of the tree, then you move both parts together. Chapter 4. Project: Moving Avatars • 42 report erratum • discuss And then you would have to repeat the same thing three more times to make a total of four trees. Four trees would be a lot of typing. Don’t forget: we pro- grammers don’t like typing. Always remember that we’re lazy. And the thing that lets us be lazy this time is a function. Also new here is color. We picked those colors from the Wikipedia list of color names at http://en.wikipedia.org/wiki/X11_color_names. The tree trunk is the color sienna. You can try your own colors if you like. The color comes from the first column on that web page, but we need to replace the # symbol on the web page with 0x so that JavaScript can read it. Thus, #A0522D becomes 0xA0522D. Now that we have a forest, let’s see if we can make the camera move with the avatar as it travels through the scene. 4.6 Moving the Camera with the Avatar Remember that to get the hands and feet to move along with our avatar, we added them to the avatar’s body instead of adding them to the scene. That is exactly what we need to do with the camera. First let’s find the line that says scene.add(camera) and delete it. Then, below the line where the avatar is added to the scene, and above the makeTreeAt() function, let’s add the camera to the avatar: var left_foot = new THREE.Mesh(foot, cover); left_foot.position.set(75, -125, 0); avatar.add(left_foot); avatar.add(camera);❶ ❶ Add this line. After hiding the code, you’ll see that when the avatar is moved, the camera stays right in front of the avatar. It’s always 500 units in front of the avatar (camera.position.z = 500). The camera is always at the same height as the avatar since we never defined the camera’s report erratum • discuss Moving the Camera with the Avatar • 43 height with position.y. The camera is always right in front since we haven’t yet set the left-right position with position.x. It might help to think of the camera being attached to the avatar with an invisible chain. Wherever the avatar goes, the camera goes as well. Pretty cool, right? Well, there is a major problem with this approach. What happens if the avatar starts cartwheeling or flipping (remember that we’re using the C and F keys for this)? Try it yourself! The avatar appears to stay still, but everything else starts spinning! (See Figure 5, Everything Starts Spinning!, on page 45.) This is because the camera is stuck on the invisible chain that’s attached to the avatar. If the avatar spins, the camera spins right along with it. (See Figure 6, The Camera Spinning with the Avatar, on page 45.) That’s not quite what we want. Instead of locking the camera on the avatar, what we really want is to lock the camera on the avatar’s position. In 3D programming there is no easy way to reliably lock something to just the position of another thing. But all is not lost. Chapter 4. Project: Moving Avatars • 44 report erratum • discuss Figure 5—Everything Starts Spinning! Figure 6—The Camera Spinning with the Avatar We’ll add an avatar position marker to the game. If we lock both the camera and the avatar to this marker, then moving the marker moves both the avatar and the camera. report erratum • discuss Moving the Camera with the Avatar • 45 But, more importantly, when the avatar does cartwheels, the camera doesn’t move. The avatar is cartwheeling, but the marker doesn’t spin. Since the marker is not spinning, the camera doesn’t spin either. In 3D programming, this marker is just a marker. It should be invisible. So we don’t want to use meshes or geometries for this. Instead we use Object3D. Let’s add the following code before the avatar-generated code, just after START CODING ON THE NEXT LINE. var marker = new THREE.Object3D(); scene.add(marker); Now we change the avatar so that it is added to the marker instead of adding it to the scene: var avatar = new THREE.Mesh(body, cover); marker.add(avatar); We also need to change how the camera is added. Instead of adding the camera to the avatar, we add it to the marker. marker.add(camera); The last thing we need to change is the keyboard event listener. Instead of changing the position of the avatar, we have to change the position of the marker. Chapter 4. Project: Moving Avatars • 46 report erratum • discuss document.addEventListener('keydown', function(event) { var code = event.keyCode; if (code == 37) marker.position.x = marker.position.x-5; // left if (code == 38) marker.position.z = marker.position.z-5; // up if (code == 39) marker.position.x = marker.position.x+5; // right if (code == 40) marker.position.z = marker.position.z+5; // down if (code == 67) is_cartwheeling = !is_cartwheeling; // C if (code == 70) is_flipping = !is_flipping; // F }); With that, we can move the avatar’s position with the keyboard, but when we flip or cartwheel, the camera stays upright. 4.7 The Code So Far If you would like to double-check your code so far, compare it to the code in Section A1.4, Code: Moving Avatars, on page 220. 4.8 What’s Next We covered a very important skill in this chapter. We’ll group objects like this over and over as our gaming skills improve. Grouping simplifies moving things together, as well as twisting, turning, growing, and shrinking things together. Before adding even more stuff to our avatar, let’s take a break so that we can explore JavaScript functions a bit more. We’re already using them to make a forest, to animate, and to listen for events. There’s even more cool stuff that we can do with them. report erratum • discuss The Code So Far • 47 CHAPTER 5 Functions: Use and Use Again We’ve come across functions more than once. Most recently we saw them in Chapter 4, Project: Moving Avatars, on page 35, where we used them to make a forest. If you were paying close attention, you may have noticed that we also used a function to build the keyboard event listener in the same chapter. Although we have used functions already, we haven’t talked much about them. You may already have a sense that they are pretty powerful, so let’s take a closer look now. We’re not going to talk about every aspect of functions—they can get quite complicated. We’ll talk about them just enough to be able to understand the functions that we use throughout the book. 5.1 Getting Started Create a new project in the ICE Code Editor. Use the Empty project template and call it Functions. After the opening Chapter 8. Project: Turning Our Avatar • 82 report erratum • discuss The Tween library animates changes between a start and end. Here we want to animate starting with one rotation and moving to an ending rotation. The first step in using Tween is to add its update() function to our animate() function: function animate() { requestAnimationFrame(animate); TWEEN.update(); walk(); turn(); acrobatics(); renderer.render(scene, camera); } animate(); Next we need to change the function turn() that we just wrote. Instead of setting the direction right away, we’ll call a new function that will spin the avatar in the new direction. Change the last line of the function turn() to call spinAvatar(): function turn() { var direction = 0; if (is_moving_forward) direction = Math.PI; if (is_moving_back) direction = 0; if (is_moving_right) direction = Math.PI/2; if (is_moving_left) direction = -Math.PI/2; spinAvatar(direction); } Last, we need to write the code for the spinAvatar() function. The Tween code might seem a little strange at first. When reading it, keep in mind that we want to start the spin where the avatar’s head is currently facing (avatar.rota- tion.y). We want to end in the new direction that is sent into spinAvatar() as the direction argument. Write the following spinAvatar() function after the turn() function: function spinAvatar(direction) { new TWEEN. Tween({y: avatar.rotation.y}). to({y: direction}, 100). onUpdate(function () { avatar.rotation.y = this.y; }). start(); } Reading from top to bottom in that function, the new Tween starts with a Y rotation value of avatar.rotation.y—the direction the avatar is already facing. We report erratum • discuss Animating the Spin • 83 then tell the Tween that we want to rotate to the new Y rotation passed to the spinAvatar() function. Every time the animation runs, the stuff inside onUpdate() is what happens. The rotation of the avatar’s head is updated to the Y rotation of the Tween. The last line starts it all. The periods at the end of each line in that function represent method chaining. In JavaScript, a semicolon ends a “sentence” of code. The period in JavaScript, unlike in English, indicates that we want to do something else with the current code—that the code sentence is not over yet. We could have put all of that code on a single line, but it can be easier for humans to read code split across lines (computers don’t care). Method chaining works with only certain JavaScript objects, like Tweens. It is a somewhat common practice in JavaScript, though we won’t be using it much ourselves. Try This Yourself We told the Tween library that it will run from start to finish in 100 milliseconds. This number was at the end of the line that started with to. It would take 1000 milliseconds to make one second, so 100 mil- liseconds is less than a second. The spin of the avatar takes less than a second. Experiment with that number to get it the way that you like. Is 1000 too long? Is 10 too short? You decide! 8.5 The Code So Far If you would like to double-check the code in this chapter, turn to Section A1.8, Code: Turning Our Avatar, on page 226. 8.6 What’s Next Wow! Our simple avatar simulation is getting quite sophisticated, isn’t it? We’ve already put quite a bit of work into our avatar, but you may have noticed that it can pass right through our trees. In the next project chapter we’ll talk about collision detection, and use it to make our avatar stop when it collides with a tree. But first it’s time to take a closer look at all of that JavaScript code that was added for us when we started this project. Chapter 8. Project: Turning Our Avatar • 84 report erratum • discuss CHAPTER 9 What’s All That Other Code? When we create a new project from the 3D starter template, there is a lot of code already in there. In this chapter we’ll see what it all means. 9.1 Getting Started Create a new project from the 3D Starter template in the ICE Code Editor. Name the project All that other code. 9.2 A Quick Introduction to HTML At the very top of our code is the following HTML: HTML is not a programming language. So what’s it doing messing up our beautiful JavaScript code? HTML is the Hypertext Markup Language. It is used to build web pages, not to make web pages do interesting things. Even though it’s not a programming language, we still need HTML for Java- Script. Since JavaScript is a web programming language, we need a web page where we can program—even if it is just a simple page. The very first line contains an opening and closing tag. In between those two tags, HTML authors would normally put writing—links to images and other pages. We’re not putting anything in there because we’re program- ming, not making web pages. To get a sense of what HTML does, add the following HTML in between the two tags, as shown: When you’re done with this chapter, you will • Know a little about making web pages • Understand the starter code • Be comfortable changing the starter code report erratum • discuss

Hello!

You can make bold words, italic words, even underlined words.

You can link to other pages. You can also add images from web servers:

Ignore ICE Warnings for HTML Your HTML code may get red X warnings. These can be safely ignored. ICE is meant to edit JavaScript, not HTML, so it can get confused. If you hide the code in the ICE Code Editor, you’ll see something like this: This is a JavaScript book, not an HTML book, but you already see some of what’s possible with HTML. After the tags come two These two lines tell the browser to load two libraries. In this case, we’re loading a 3D JavaScript library named Three.js and a small library that fixes some ICE Code Editor bugs in the Chrome web browser. Chapter 9. What’s All That Other Code? • 86 report erratum • discuss JavaScript doesn’t have to come from other locations. For most of this book, we’re coding inside an HTML web page. 9.3 Setting the Scene To do anything in 3D programming, we need a scene. Think of the scene as the universe in which all of the action is going to take place. For something so important, it’s really easy to create. The following code in ICE does just that: // This is where stuff in our game will happen: var scene = new THREE.Scene(); Scenes are really simple to work with. We’ve been adding objects to them throughout the book. Once things have been added to a scene, it’s the scene’s job to keep track of everything. In fact, that’s pretty much all we need to know about scenes—after creating one, we add lots of stuff to it and the scene takes care of the rest. 9.4 Using Cameras to Capture the Scene Scenes do a great job of keeping track of everything, but they don’t show us what’s happening. To see anything in the scene, we need a camera. Notice the following code in ICE: // This is what sees the stuff: var aspect_ratio = window.innerWidth / window.innerHeight; var camera = new THREE.PerspectiveCamera(75, aspect_ratio, 1, 10000); camera.position.z = 500; scene.add(camera); The purpose of the aspect_ratio is to determine the shape of the browser. This is the same thing as aspect ratios for movie screens and TV sets. A large TV with a 4:3 aspect ratio might be four meters wide and three meters tall (OK that’s a really large TV). An even larger 4:3 screen might be twelve meters wide and nine meters tall (multiply both the 4 and 3 in 4:3 by 3 to get 12:9). Most movies today are made at an aspect ratio of 16:9, which would mean a nine-meter-tall screen would be sixteen meters wide—four extra meters when compared with the same-height 4:3 aspect ratio. Why does this matter for us? If you try to project a movie made in 16:9 onto a 4:3 screen, a lot of squishing has to be done. Similarly, a 4:3 movie would need to be stretched to be shown on a 16:9 screen. Instead of stretching or squishing, most movies are chopped so that you miss those four meters of action. Our Three.js library doesn’t chop—it stretches or squishes. In other words, it’s pretty important to get the aspect ratio right. report erratum • discuss Setting the Scene • 87 After we build a new camera, we need to add it to the scene. Like anything else in 3D programming, the camera is placed at the center of the scene to which we add it. We move it 500 units away from the center in the Z direction (“out” of the screen) so that we have a good view of what’s going on back at the center of the scene. 9.5 Using a Renderer to Project What the Camera Sees The scene and the camera are enough to describe how the scene looks and from where we’re viewing it, but one more thing is required to show it on the web page. This is the job of the renderer. It shows, or renders, the scene as the camera sees it: // This will draw what the camera sees onto the screen: var renderer = new THREE.CanvasRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); We have to tell the renderer the size of the screen to which it will be drawing. We set the size of the view to take up the whole browser (window.innerWidth and window.innerHeight). To include the renderer in the web page, we use its domElement property. A domElement is another name for an HTML tag like those we added earlier in the chapter. Instead of holding a title or paragraph, this domElement holds our amazing 3D worlds. We add that domElement to the document.body—which is the same tag that held the HTML from earlier. The appendChild() function takes care of adding the domElement to the document body. If you’re wondering why we have names like appendChild() and domElement, all I can tell you is to be glad you are a 3D-game programmer, not a web programmer. Web programmers have to use silly (and hard-to-remember) names like this all the time. At this point, the renderer can draw to the screen, but we still need to tell it to render before anything will show up. This is where renderer.render() comes into play at the end of your current code. // Now, show what the camera sees on the screen: renderer.render(scene, camera); It might seem as though the renderer is an obnoxious younger brother or sister, doing the right thing only when we’re extremely specific in our instructions. In a sense this is true, but in another sense all programming is like this. Until we tell the computer in exactly the right way to do something, it often does something completely unexpected. Chapter 9. What’s All That Other Code? • 88 report erratum • discuss In the case of the renderer, we can already see why it’s nice to have this kind of control. In some of our experiments, we rendered just a single time. But in many of our projects, we render repeatedly inside an animate() function. Without this kind of control, it would be much harder to pick and choose the right rendering style. 9.6 Exploring Different Cameras and Renderers You may have noticed that we call our camera a PerspectiveCamera and our renderer a CanvasRenderer. If these names seem oddly specific, that’s because there are other kinds of cameras and renderers. We’ve been using these because most browsers and hardware support them. As we’ll see in Chapter 12, Working with Lights and Materials, on page 109, some cool effects that we might want to add to our 3D games require different cameras and renderers that work only on relatively new computers. You Don’t Have to Do These Examples Some computers will not be able to run the examples in the rest of the chapter. This is because they rely on a technology called WebGL, which we will talk about in more detail in Chapter 12, Working with Lights and Materials, on page 109. Since your computer might not support WebGL, you don’t have to follow along in the ICE Code Editor in this section. Introducing the WebGL Renderer The other important renderer is the WebGLRenderer. We use it exactly the same way that we use the CanvasRenderer. We only need to change the name: // This will draw what the camera sees onto the screen: var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); WebGL is a fairly new technology that allows programmers to perform inter- esting 3D-programming techniques like lighting, shadows, and fog. It also runs animations much faster than is possible with the CanvasRenderer. We’ll explore it more in Working with Lights and Materials. A Quick Peek at a Weirdly Named Camera The other kind of camera is called orthographic. To understand what an orthographic camera does, we can add a red road on which the purple fruit monster can travel. Add the following after START CODING ON THE NEXT LINE. report erratum • discuss Exploring Different Cameras and Renderers • 89 var shape = new THREE.CubeGeometry(200, 1000, 10); var cover = new THREE.MeshBasicMaterial({color:0x990000}); var road = new THREE.Mesh(shape, cover); scene.add(road); road.position.set(0, 400, 0); road.rotation.set(-Math.PI/4, 0, 0); Our perspective camera makes the road look something like this: This is a rectangular road, but it doesn’t look rectangular. It looks as though it’s getting smaller the farther away it gets. The perspective camera does this for us: var aspect_ratio = window.innerWidth / window.innerHeight; var camera = new THREE.PerspectiveCamera(75, aspect_ratio, 1, 10000); If we use an orthographic camera, on the other hand, everything looks flat: Chapter 9. What’s All That Other Code? • 90 report erratum • discuss That is the same road from the previous image. We’ve only replaced the two lines that create the perspective camera with the following: var width = window.innerWidth, height = window.innerHeight; var camera = new THREE.OrthographicCamera( -width/2, width/2, height/2, -height/2, 1, 10000 ); As you might imagine, the perspective camera that gives everything a three- dimensional feel is very handy in 3D games. Why would you want to use an orthographic camera? Orthographic cameras are useful in two cases. The first is when you want to make a flat, 2D game. Using a 3D camera for a flat game just looks weird—especially at the edges of the screen. The other is when we make games with really, really long distances, such as space games. In fact, we can use orthographic cameras in some of the space simulations we’ll do in a little while. 9.7 What’s Next Now that we understand all about cameras, scenes, and JavaScript libraries, we’ll change them more and more. But first let’s teach our game avatar to not walk through trees. report erratum • discuss What’s Next • 91 CHAPTER 10 Project: Collisions We have a pretty slick game avatar. It moves, it walks, it even turns. But you may have noticed something odd about our avatar. It can walk through trees. In this chapter we’ll use tools that are built into our Three.js 3D JavaScript library to prevent the avatar-in-a-tree effect. (As we’ll see in other chapters, there are other ways to do the same thing.) 10.1 Getting Started If it’s not already open in the ICE Code Editor, open the project from Project: Turning Our Avatar that we named My Avatar: Turning. Make a copy of our avatar project. From the menu in the ICE Code Editor, select Make a Copy and enter My Avatar: Collisions as the new project name. When you’re done with this chapter, you will • Be able to stop game elements from moving through each other • Understand collisions, which are important in gaming • Have game boundaries for your avatar report erratum • discuss 10.2 Rays and Intersections The way we prevent our avatar from walking through trees is actually quite simple. Imagine an arrow pointing down from our avatar. In geometry, we call an arrow point a ray. A ray is what you get when you start in one place and point in a direction. In this case, the place is where our avatar is and the direction is down. Sometimes giving names to such simple ideas seems silly, but it’s important for programmers to know these names. Programmers Like to Give Fancy Names to Simple Ideas Knowing the names for simple concepts makes it easier to talk to other people doing the same work. Programmers call these names patterns. Now that we have our ray pointing down, imagine circles on the ground around our trees. Here is the crazy-simple way that we prevent our avatar from running into a tree: we don’t! Instead, we prevent the avatar’s ray from pointing through the tree’s circle. Chapter 10. Project: Collisions • 94 report erratum • discuss If, at any time, we find that the next movement would place the avatar’s ray so that it points through the circle, we stop the avatar from moving. That’s all there is to it! Star Trek II: The Wrath of Khan It may seem strange, but watching certain science-fiction movies will make your life easier as a programmer. Sometimes programmers say odd things that turn out to be quotes from movies. It is not a requirement to watch or even like these movies, but it can help. One such quote is from the classic Star Trek II: The Wrath of Khan. The quote is “He is intelligent, but not experienced. His pattern indicates two-dimensional thinking.” The bad guy in the movie was not accustomed to thinking in three dimensions, and this was used against him. In this case, we want to think about collisions in only two dimensions even though we are building a three-dimensional game. We’re thinking about colli- sions only in two dimensions (X and Z), completely ignoring the up-and-down Y dimension. This is yet another example of cheating whenever possible. Real 3D collisions are difficult and require new JavaScript libraries. But we can cheat and get the same effect in many cases using easier tricks. At this point, a picture of what to do next should be forming in your mind. We’ll need a list of these tree-circle boundaries that our avatar won’t be allowed to enter. We’ll need to build those circle boundaries when we build the trees, and detect when the avatar is about to enter a circle boundary. Last, we need to stop the avatar from entering these forbidden areas. report erratum • discuss Rays and Intersections • 95 Let’s establish the list that will hold all forbidden boundaries. Just below the START CODING ON THE NEXT LINE line, add the following. var not_allowed = []; Recall from Section 7.5, Listing Things, on page 77, that square brackets are JavaScript’s way of making lists. Here, our empty square brackets create an empty list. The not_allowed variable is an empty list of spaces in which the avatar is not allowed. Next, find where makeTreeAt() is defined. When we make our tree, we’ll make the boundaries as well. Add the following code after the line that adds the treetop to the trunk, and before the line that sets the trunk position. var boundary = new THREE.Mesh( new THREE.CircleGeometry(300), new THREE.MeshNormalMaterial() ); boundary.position.y = -100; boundary.rotation.x = -Math.PI/2; trunk.add(boundary); not_allowed.push(boundary); There’s nothing superfancy there. We create our usual 3D mesh—this time with a simple circle geometry. We rotate it so that it lays flat and position it below the tree. And, of course, we finish by adding it to the tree. But we’re not quite done with our boundary mesh. At the end, we push it onto the list of disallowed spaces. Now every time we make a tree with the makeTreeAt() function, we’re building up this list. Let’s do something with that list. At the very bottom of our code, just above the tag, add the following code to detect collisions. function detectCollisions() { var vector = new THREE.Vector3(0, -1, 0); var ray = new THREE.Ray(marker.position, vector); var intersects = ray.intersectObjects(not_allowed); if (intersects.length > 0) return true; return false; } This function returns a Boolean—a yes-or-no answer—depending on whether the avatar is colliding with a boundary. This is where we make our ray to see if it points through anything. As described earlier, a ray is the combination of a direction, or vector (down in our case), and a point (in this case, the Chapter 10. Project: Collisions • 96 report erratum • discuss avatar’s marker.position). We then ask that ray if it goes through (intersects) any of the not_allowed objects. If the ray does intersect one of those objects, then the intersects variable will have a length that is greater than zero. In that case, we have detected a collision and we return true. Otherwise, there is no collision and we return false. Collisions are a tough problem to solve in many situations, so you’re doing great by following along with this. But we’re not quite finished. We can now detect when an avatar is colliding with a boundary, but we haven’t actually stopped the avatar yet. Let’s do this in the keydown listener. In the keydown listener, if an arrow key is pressed, we change the avatar’s position. if (code == 37) {// left marker.position.x = marker.position.x-5; is_moving_left = true; } Such a change might mean that the avatar is now in the boundary. If so, we have to undo the move right away. Add the following code at the bottom of the keydown event listener (just after the if (code == 70)). if (detectCollisions()) { if (is_moving_left) marker.position.x = marker.position.x+5; if (is_moving_right) marker.position.x = marker.position.x-5; if (is_moving_forward) marker.position.z = marker.position.z+5; if (is_moving_back) marker.position.z = marker.position.z-5; } Read through these lines to make sure you understand them. That bit of code says if we detect a collision, then check the direction in which we’re moving. If we’re moving left, then reverse the movement that the avatar just did—go back in the opposite direction the same amount. With that, our avatar can walk up to the tree boundaries, but go no farther. report erratum • discuss Rays and Intersections • 97 Yay! That might seem like some pretty easy code, but you just solved a very hard problem in game programming. 10.3 The Code So Far In case you would like to double-check the code in this chapter, it’s included in Section A1.10, Code: Collisions, on page 230. 10.4 What’s Next Collision detection in games is a really tricky problem to solve, so congratula- tions on getting this far. It gets even tougher once you have to worry about moving up and down in addition to left, right, back, and forward. But the concept is the same. Usually we rely on code libraries written by other people to help us with those cases. In some of the games we’ll experiment with shortly, we’ll use just such a code library. But first we’ll put the finishing touch on our avatar game. In the next chapter we’ll add sounds and scoring. Let’s get to it! Chapter 10. Project: Collisions • 98 report erratum • discuss CHAPTER 11 Project: Fruit Hunt We have an avatar. We have trees. Let’s make a game in which our avatar has to get stuff out of those trees. The trees are hiding yummy fruit that the avatar wants. And if the avatar can get to the fruit in time, points will be added to the scoreboard. It will end up looking something like this: Congratulations to fellow game programmer Sophie H. for coming up with the winning game concept used in this chapter! 11.1 Getting Started To make this game, we need the avatar, the trees, and the collision-detection functions that we’ve been working on throughout this book. After Chapter 10, Project: Collisions, on page 93, we have everything that we need to get started on this project. So let’s make a copy of that project we’ve been working on. When you’re done with this chapter, you will • Be able to add sounds to games • Be able to add simple scoring to a game • Have a silly game to play report erratum • discuss From the menu in the ICE Code Editor, select Make a Copy and enter Fruit Hunt! as the new project name. To keep score in this game, we’ll need something new—a scoreboard. There is a fairly nice scoreboard built into the ICE Code Editor, but we have to load the library first. In Chapter 9, What's All That Other Code?, on page 85, we looked at the libraries that are loaded with the Since this is just the “getting started” section of our program, those lines won’t actually change anything in the game. To see the scoreboard, we need to configure it and turn it on. Let’s do that next. 11.2 Starting a Scoreboard at Zero The rest of the code in this chapter will go below the START CODING ON THE NEXT LINE line. To add a scoreboard to our game, we create a new one using the new keyword. Then we tell the scoreboard to start a countdown timer, show the score, and add a help message. To do all of that, we first enter the following code after the line that introduces the not_allowed variable: var scoreboard = new Scoreboard(); scoreboard.countdown(45); scoreboard.score(); scoreboard.help( 'Arrow keys to move. ' + 'Space bar to jump for fruit. ' + 'Watch for shaking trees with fruit.' + 'Get near the tree and jump before the fruit is gone!' ); These lines add a nifty-looking scoreboard to our screen, complete with the time remaining in the game (45 seconds), the current score (zero), and a hint to players that they can press the question mark (?) key to get some help. The scoreboard should look like the following: Chapter 11. Project: Fruit Hunt • 100 report erratum • discuss Before making the game behave the way the help text says it should, we need to teach the game what to do when time runs out. To do so, add the following on the line after all of the scoreboard code: var game_over = false; scoreboard.onTimeExpired(function() { scoreboard.message("Game Over!"); game_over = true; }); This tells the scoreboard that when time expires, it should run the function that sets the scoreboard message to Game Over! and that the game_over variable should be set to true. That’s all there is to build the scoreboard. Now, let’s figure out a way for the player to add points to the scoreboard. 11.3 Giving Trees a Little Wiggle The goal of this game will be to find fruit, which we’ll call treasure, in the trees. At any given time, only one tree will have treasure. To show which tree it is, we’ll give it a little shake. But first we need a list of trees. Find the code that created the forest—there should be four lines that make different makeTree() calls. We added this way back in Chapter 4, Project: Moving Avatars, on page 35. We need to make a little change to the part of the code that adds the trees to the scene. We start by adding two variables. The first is tree_with_treasure, which you might have guessed will point to the tree that currently has treasure. The second variable is a list containing all the trees, which we’ll call trees. We then push into this list all the trees that will make up our forest. Change your four makeTreeAt() lines to the following: var tree_with_treasure; var trees = []; trees.push(makeTreeAt( 500, 0)); trees.push(makeTreeAt(-500, 0)); trees.push(makeTreeAt( 750, -1000)); trees.push(makeTreeAt(-750, -1000)); To make this bit work, we need to change the makeTreeAt() function so that it returns something. If makeTreeAt() doesn’t return anything, then we would be report erratum • discuss Giving Trees a Little Wiggle • 101 pushing nothing onto our list of trees. Add the following line to the very bottom of the makeTreeAt() function before the last curly brace: function makeTreeAt(x, z) { // Don't change any code at the start... // ... but add the following line to the end: return top; } With that, the treetop (the green ball/leaves) is returned to be added to the list of trees. We could have returned the trunk or even the collision boundary that we added in Project: Collisions. The top of the tree is what we need to work with the most (as that is where the treasure will be hidden), so it makes sense to return it so that it can be pushed into the list of trees. Now that we have a list of trees, we can hide treasure in one and shake it. After the makeTreeAt() function, add the following function and function call: function shakeTree() { tree_with_treasure = Math.floor(Math.random() * trees.length); new TWEEN .Tween({x: 0}) .to({x: 2*Math.PI}, 200) .repeat(20) .onUpdate(function () { trees[tree_with_treasure].position.x = 75 * Math.sin(this.x); }) .start(); setTimeout(shakeTree, 12*1000); } shakeTree(); We’ll talk about Math.floor() and Math.random() in Chapter 20, Project: River Rafting, on page 185. For now, let’s leave it that the first line in shakeTree() picks a random tree. We’ve already met the Tween library, which moves things from one value to another. In this case, we again move along a sine curve. Sines and cosines are great because they start and end at the same value when moving from zero to 360° (2*Math.PI). We use the sine. As the value of the Tween moves from 0 to 2*Math.PI, the value of Math.sin() goes from 0 to 1, then back to 0, then to -1, and finally back to 0. In other words, it’s perfect to make things wiggle a little. Chapter 11. Project: Fruit Hunt • 102 report erratum • discuss The last part of shakeTree() sets a timeout for 12 seconds. After 12 seconds have passed, this timeout calls the shakeTree() function again and assigns a new tree with the treasure. After this code, a different tree should be wiggling uncontrollably telling the player that there is treasure to be collected. Let’s give the avatar a way to grab that treasure. 11.4 Jumping for Points In this game, the avatar needs to jump next to the current treasure-filled tree. We’ll do two things when this happens: the avatar will score some points and we’ll make a nice little animation of the treasure and play a sound. But first we need a key that will start a jump. We do this by adding the follow- ing if statement to the keydown() listener: if (code == 32) jump(); // space You can add that if code just above the other if statements that turn the avatar. Now we add the jump() function that the case statement calls. This function can go after the detectCollisions() function. It checks for treasure and animates the jump on our screen: function jump() { checkForTreasure(); animateJump(); } To check whether the avatar is close enough to grab treasure, add the following function at the bottom of the code (just above the last ❶ We’re going to use physics in this game. We use the Physijs library so we don’t have to write all the physics code ourselves. ❷ This library will help keep score in the game. Then, at the top of the code from the 3D starter project template, just below the It’s important that the Mouse.js There are all sorts of things that you can do to make this game as special as possible for players. Get creative! 19.4 The Code So Far If you would like to double-check the code in this chapter, compare yours with the code in Section A1.19, Code: Multilevel Game, on page 259. 19.5 What’s Next Most of the action in this game took place in two dimensions. To be sure, we used some impressive programming skills (and even a little 3D programming) to make this game. Still, let’s get back to 3D programming in our next game. We’ll use all of our skills in the next chapter, so let’s go! Chapter 19. Project: Multilevel Game • 184 report erratum • discuss CHAPTER 20 Project: River Rafting For our final project, let’s build a river-rafting game in which the player needs to navigate the raft along a river, dodging obstacles and picking up bonus points where possible. The game will look something like this sketch: We’ll also add a few goodies of our own. 20.1 Getting Started We start by creating a new project in the ICE Code Editor. We use the 3D starter project (with Physics) template and call it River Rafting. We’ll want a scoreboard for the game, so let’s insert a new line after line 4, just before the plain When you’re done with this chapter, you will • Have made a full 3D game • Be able to add scoring to games • Understand how to warp shapes into something new report erratum • discuss After the tag). report erratum • discuss Build a Raft for Racing • 197 document.addEventListener("keydown", function(event) { var code = event.keyCode; if (code == 32) pushRaft(); // space if (code == 37) rotateRaft(-1); // left if (code == 39) rotateRaft(1); // right if (code == 82) startGame(raft, river, scoreboard); // R }); function pushRaft() { var angle = raft.rotation.z; raft.applyCentralForce( new THREE.Vector3( 500 * Math.cos(angle), 0, -500 * Math.sin(angle) ) ); } function rotateRaft(direction) { raft.__dirtyRotation = true; raft.rotation.z = raft.rotation.z + direction * Math.PI/10; } We’ve seen keyboard listeners a lot by this point, so document.addEventListener() should already be familiar. The pushRaft() function uses a new method for physics objects: applyCentralForce(). This is just a fancy way of saying “push a thing from the middle and not the edge.” Lastly, the rotation, including __dirtyRotation, should be familiar—we last saw it in Chapter 18, Project: Cave Puzzle, on page 165. With that, we have the basic pieces of a pretty cool game! The left and right arrow keys will turn the raft and the space bar will push the raft forward in the direction it’s facing. We can do a lot more with this game. We’ll add simple scoring and an obstacle or two in the river. 20.5 Setting the Finish Line Eventually the raft reaches the finish line. And then it keeps right on going. And going. Instead, let’s pause the game so that players can take a moment to admire their score before trying again. We need to make changes in four places: in our code outline and in startGame(), animate(), and gameStep(). Let’s start with the code outline. Before the call to the startGame() function, we need to add a line for the paused variable: Chapter 20. Project: River Rafting • 198 report erratum • discuss var paused; startGame(raft, river, scoreboard); Other functions will use that variable to decide if they need to animate or update the game. JavaScript is pretty uptight about when variables are declared. The rule of thumb is that variables need to be declared before they’re used. The paused variable will be used when startGame(), animate(), and gameStep() are called, so we declare it before any of them are called. We set paused for the first time in the startGame() function. Whenever the game is started, which is what startGame() does, the game shouldn’t be paused. So we set paused to false at the bottom of startGame(): function startGame(raft, river, scoreboard) { var start = river.river_points[100]; raft.__dirtyPosition = true; raft.position.set(start.y, start.z + 100, 0); raft.setLinearVelocity(new THREE.Vector3()); scoreboard.resetTimer(); scoreboard.score(0); scoreboard.message(''); updateCamera(); camera.lookAt(new THREE.Vector3(start.y, 0, 0)); paused = false; } Next we tell the animate() function that it doesn’t have to render the scene when the game is paused. That is, if paused is set to true, then we exit the animate() function before updating the camera or rendering the scene: function animate() { requestAnimationFrame(animate); if (paused) return; updateCamera(); renderer.render(scene, camera); } We check for paused after calling requestAnimationFrame() so the animation function will continue to work—even though it’s not doing anything. This way when the game is reset and paused is set to true, the animation is still running and the computer can update the camera without any extra work. We do something similar in the gameStep() function. If the game is paused, then we exit immediately from the function without completing any of the usual steps: report erratum • discuss Setting the Finish Line • 199 function gameStep() { // Update physics 60 times a second so that motion is smooth setTimeout(gameStep, 1000/60); if (paused) return; checkForGameOver(); scene.simulate(); } Note that we’ve changed the order of the function calls in gameStep() to work with pausing. The scene.simulate() and checkForGameOver() calls both come after the if (paused) statement—there’s no sense in simulating physics or checking if the game is over when the game is paused. The checkForGameOver() function is new. It can go right after the gameStep() function and should look like this: function checkForGameOver() { if (raft.position.x < 250) return; paused = true; scoreboard.stopTimer(); scoreboard.message("You made it!"); } If the raft’s X position has not reached the finish line, or when X is 250, then this function does nothing—it returns immediately and nothing else happens. If the raft has reached the finish line, then we set paused to true so that all the other functions can stop working. We also stop the scoreboard timer and add a message to display. The game should pause at the end of the river and display the time it took the player to complete the race, and a message of “You made it!” You might even be able to make it pretty fast: Scoring Points by Distance In some games, a player receives points simply for making it further away from the starting point. Keeping score is something that belongs in the gameStep() method because it’s game logic, rather than animation, which would belong in the animate() function. Chapter 20. Project: River Rafting • 200 report erratum • discuss If you want to increase the score as the raft makes its way along the river, we can add a call to the updateScore() function in gameStep(): function gameStep() { // Update physics 60 times a second so that motion is smooth setTimeout(gameStep, 1000/60); if (paused) return; updateScore(); checkForGameOver(); scene.simulate(); } Due to the way we have the river scene rotated, the raft’s X position changes as it moves down the river. To increase the score as the raft reaches the next 25 units of distance, we can use the following (the code goes below the gameStep() function): var next_x; function updateScore() { if (!next_x) next_x = raft.position.x + 25; if (raft.position.x > next_x) { scoreboard.addPoints(10); next_x = next_x + 25; } } Each time the raft reaches the next X scoring point, this function adds ten points to the score and recalculates the next X scoring area to be 25 units further away. Another distance-based scoring feature we can add is a time bonus for finish- ing the rafting course within a certain amount of time. We do this by adding the last three lines shown here to the checkForGameOver() function: function checkForGameOver() { if (raft.position.x < 250) return; paused = true; scoreboard.stopTimer(); scoreboard.message("You made it!"); if (scoreboard.getTime() < 30) scoreboard.addPoints(100); if (scoreboard.getTime() < 25) scoreboard.addPoints(200); if (scoreboard.getTime() < 20) scoreboard.addPoints(500); } If the player finishes in less than 30 seconds, an additional 100 points are awarded. If the player finishes in less than 25 seconds, then both the 100 report erratum • discuss Setting the Finish Line • 201 points and 200 more are awarded. If the player finishes in less than 20 sec- onds, then the 100 and the 200 points are awarded, along with an additional 500 points, for a possible total of 800 extra points to be won. Can you do it? Power-Up Points Many games reward a player for capturing bonus items. If we want to do that in our raft game, we have to do two things: add those items to the river and add to the player’s score when the raft bumps into those objects. We’ll want to add items to the game whenever it gets reset, so much of our work needs to take place in and after the startGame() function. But first, we need to declare a variable to hold the list of items in the game. Add it before the paused variable and the first call to startGame() in the code outline: var game_items = []; var paused; startGame(raft, river, scoreboard); Then, in the definition of startGame(), add a call to resetItems(): function startGame(raft, river, scoreboard) { var start = river.river_points[100]; raft.__dirtyPosition = true; raft.position.set(start.y, start.z + 100, 0); raft.setLinearVelocity(new THREE.Vector3()); scoreboard.resetTimer(); scoreboard.score(0); scoreboard.clearMessage(); updateCamera(); camera.lookAt(new THREE.Vector3(start.y, 0, 0)); resetItems(river, scoreboard); paused = false; } Since the call to resetItems() comes after the camera changes, the definition of the resetItems() function should come after camera functions like updateCamera(). The definition of this function is simple enough. It calls two other func- tions—one to remove all existing items from the screen and the other to add new items to the screen: function resetItems(ground, scoreboard) { removeItems(); addItems(ground, scoreboard); } Removing items from the game is a simple matter of removing each one from the scene. Once each item has been removed from the scene, we can set the list of game items to an empty list: Chapter 20. Project: River Rafting • 202 report erratum • discuss function removeItems() { game_items.forEach(function(item) { scene.remove(item); }); game_items = []; } Adding items to the river is where things start to get interesting. The ground still has the river_points property. We’ll use that list of points to randomly place power-up fruit at a couple of places along the river. Randomly placing the fruit will make each new game a different challenge for players. Only we don’t want to be quite random about it. If it were completely random, we might end up with two pieces of fruit in the same place or one right at the start. Recall that there are 100 faces in the ground and 100 points in the ground that we are using to describe the middle of the river. Lets randomly place a power-up fruit around river point 20 and another around point 70. The fol- lowing will do what we need: function addItems(ground, scoreboard) { var points = ground.river_points; var random20 = Math.floor(20 + 10*Math.random()), fruit20 = addFruitPowerUp(points[random20], ground, scoreboard); game_items.push(fruit20); var random70 = Math.floor(70 + 10*Math.random()), fruit70 = addFruitPowerUp(points[random70], ground, scoreboard); game_items.push(fruit70); } The main purpose of this code block is to call the addFruitPowerUp() function that we’ll build shortly. The fruit20 and fruit70 items are then pushed onto the list of all game items (so that they can later be removed as needed). The random20 and random70 numbers might look a little complicated at first, but if you look closely, they ought to make some sense. Let’s look at just ran- dom20 to better understand. The Math.random() function generates a number between 0 and 1. • If Math.random() is 0, then 10*Math.random() is 0, making 20 + 10*Math.random() end up as 20. • If Math.random() is 0.5, then 10*Math.random() is 5, making 20 + 10*Math.random() end up as 25. report erratum • discuss Setting the Finish Line • 203 • If Math.random() is 1, then 10*Math.random() is 10, making 20 + 10*Math.random() end up as 30. In other words, since Math.random() is guaranteed to be between 0 and 1, we’re guaranteed of getting a number between 20 and 30. The random numbers are wrapped in a Math.floor() function call. Math.floor() removes everything after the decimal point. If Math.random() returns 0.01, then 10*Math.random() would wind up as 0.1. Math.floor() removes the decimal point and everything after it, leaving us with 0. The random number fetches a point from the river_points property to send to addFruitPowerUp(). That function mostly does stuff that we’ve seen before—it builds a physical mesh, assigns a collision event listener, and adds the yellow fruit to the scene: function addFruitPowerUp(location, ground, scoreboard) { var mesh = new Physijs.ConvexMesh( new THREE.SphereGeometry(10, 25), new THREE.MeshPhongMaterial({emissive: 0xbbcc00}), 0 ); mesh.receiveShadow = true; mesh.castShadow = true; mesh.addEventListener('collision', function() { var list_index = game_items.indexOf(mesh); game_items.splice(list_index, 1); scene.remove(mesh); scoreboard.addPoints(200); scoreboard.message('Yum!'); setTimeout(function() {scoreboard.clearMessage();}, 2.5* 1000); }); ground.updateMatrixWorld(); var p = new THREE.Vector3(location.x, location.y, -20); ground.localToWorld(p); mesh.position.copy(p); scene.add(mesh); return mesh; } The first thing we do in the collision-handling function is remove the fruit from the list of game items. JavaScript doesn’t make this easy—we get the index (the location in the list) of the item to be removed, then remove it by splicing from that index to the one following it. There is a famous JavaScript book named JavaScript: The Good Parts—removing things from lists is defi- nitely not in that book (which you should read). Chapter 20. Project: River Rafting • 204 report erratum • discuss Also new to us is converting from local coordinates to world coordinates with the localToWorld() method. For most of this book we have found it very useful to work in a frame of reference—it’s an invaluable trick for 3D programmers. Every now and then we have to put things back in the regular scene coordi- nates. This is one of those times. The localToWorld() method gives us the scene coordinates for the random river points so that when the fruit is added to the scene, it looks as though it was added to the river. Before a localToWorld() call, it’s a good idea to call updateMatrixWorld() on the thing whose world coordinates we need. A matrix is a mathematical way to describe position, direction, and other values in 3D. The updateMatrixWorld() call ensures that these values are all up-to-date and accurate. With that, we have two pieces of fruit that can help you score points like crazy while playing the game. You might even be able to beat my high score: 20.6 The Code So Far If you would like to double-check the code in this chapter, turn to Section A1.20, Code: River Rafting, on page 265. 20.7 What’s Next That was a lot of code. But it was worth it. We put many of our skills to use with physics, lights, and materials. We also saw glimpses of what else is possible in 3D programming by pulling vertices of shapes and converting local coordinates to “world” coordinates. Like our other games, do not consider this one final. There’s still plenty that you can add. Maybe you can incorporate obstacles that take away points? Add some jumps? Make this a multilevel game as we did in Chapter 19, Project: Multilevel Game, on page 177? Make the course longer? You might try adding camera controls so you can see from the viewpoint of a raft passenger instead of viewing everything from above. Or maybe you already have ideas for how to improve this game that I can’t even begin to think of. In our final chapter we’ll switch gears to putting our projects out on the Web. report erratum • discuss The Code So Far • 205 CHAPTER 21 Getting Code on the Web Since we’re programming a web language, it’s worth a quick look at how the Web works. We’re not going into too much detail here—just enough for us to understand why we do some of the things that we do in this book. An Abstraction Is Worth a Thousand Words You may have heard the phrase “a picture is worth a thousand words.” Programmers like you and me do a lot of work in our brains. When we’re thinking about a problem or trying to come up with a cool new way of doing something, we use mental pictures of the problem. These pictures in our brains are called abstractions. Abstractions don’t always have a ton of detail. They usually have just enough to help us understand the problem. An abstraction for a cloud might be that it’s made up of a whole bunch of cotton balls. That’s enough of a mental picture to understand the shape and appearance of a cloud, and sometimes that is all we need. But abstractions don’t always suffice. If we try to understand why a cloud produces rain, the idea that clouds are made of cotton balls won’t help at all. Keep this in mind as we talk about the Web. We’re using abstrac- tions, and they will help most of the time. But sometimes they will be wet cotton balls. When you’re done with this chapter, you will • Have a better idea of the parts that make up a website • Understand what we need to build our own websites • Know how to put a project on a site like Tumblr report erratum • discuss 21.1 The Mighty, Mighty Browser Behold the mighty browser: A web browser is an extraordinarily complex piece of technology that we use every day. Amazingly, it’s also pretty dumb about some things. When we tell a browser that we want a website or a page on a website, it sends a request through the Internet to a publicly available machine: As you can see, when you ask your browser to show a site, your browser makes a request through the Internet. This request asks one particular web server for information that it has. That information might be an HTML web page, it might be an image, it might be a movie, and it might be JavaScript. To reach the right server, our browser has to look up the public Internet address of the web server. Google’s Internet address for www.google.com is 173.194.73.147. The numbers in the address are enough for the Internet to get the browser’s request to the web server. Chapter 21. Getting Code on the Web • 208 report erratum • discuss Web Servers Must Be Publicly Available on the Internet Remember that the machine holding a website must be publicly available on the Internet. The machines that you use at home are almost never publicly available. Even if someone else on the Internet knows your machine’s network address, they would still not be able to reach it because it’s not publicly available. Unfortunately, this means you usually need to pay a little money to get your cool web games available on the Internet. You need to pay a web hosting company to host your games. When the browser’s request reaches the web server, the server checks to see if it has the requested item. Web servers can have all sorts of information stored on them: Usually the first request that a browser sends to a server is for an HTML web page: If the server has the web page the user is looking for, then it sends it back to the browser: report erratum • discuss The Mighty, Mighty Browser • 209 This is the kind of dumb part. The web page is usually pretty small and uninteresting. Web pages often look pretty and do lots of amazing things, but by themselves they don’t look pretty. A web page by itself can’t do lots of amazing things. For anything fun to happen, the web page needs to tell the browser to make lots and lots of other requests. We saw HTML web pages in Chapter 9, What's All That Other Code?, on page 85. They have funny angle brackets that are the markup important to browsers:

Hello!

You can make bold words, italic words, even underlined words.

You can link to other pages. You can also add images from web servers:

Chapter 21. Getting Code on the Web • 210 report erratum • discuss Some markup has things like JavaScript files or images or styles that make cool stuff happen. And so, as soon as the browser gets the web page that it asked for, it has to ask for tons and tons more things: And here is the really dumb part about all of this: the browser usually has to wait until most or all of these things come back before it does anything important. There are two ways to wait for things to be ready before doing important work. The first is what we’ve been doing—putting the most important stuff last in the HTML document: In this case, we have the A1.2 Code: Playing with the Console and Finding What’s Broken There was no working code from Chapter 2, Playing with the Console and Finding What’s Broken, on page 17. We wrote some broken code in ICE and explored the JavaScript console. Appendix 1. Project Code • 218 report erratum • discuss A1.3 Code: Making an Avatar This is the final version of the avatar code from Chapter 3, Project: Making an Avatar, on page 25. A1.4 Code: Moving Avatars This is the moving-avatar code from Chapter 4, Project: Moving Avatars, on page 35. A1.5 Code: Functions: Use and Use Again We intentionally broke a lot of things as we explored functions in Chapter 5, Functions: Use and Use Again, on page 49. A copy of the code that works follows (note that it doesn’t include the recursion example). A1.6 Code: Moving Hands and Feet This is the code from Chapter 6, Project: Moving Hands and Feet, on page 59. A1.7 Code: A Closer Look at JavaScript Fundamentals There was no project code in Chapter 7, A Closer Look at JavaScript Funda- mentals, on page 67. A1.8 Code: Turning Our Avatar This is the code from Chapter 8, Project: Turning Our Avatar, on page 79. A1.9 Code: What’s All That Other Code? There was no new code in Chapter 9, What's All That Other Code?, on page 85. We only explored the code that is automatically created when we start new projects. report erratum • discuss Code: What’s All That Other Code? • 229 A1.10 Code: Collisions This is the avatar code after we added collisions in Chapter 10, Project: Colli- sions, on page 93. report erratum • discuss Code: Collisions • 233 A1.11 Code: Fruit Hunt This is the avatar code after we added it to the fruit-hunt game in Chapter 11, Project: Fruit Hunt, on page 99. This code uses WebGLRenderer to make the trees a little prettier, but the CanvasRenderer should work nearly as well. A1.12 Code: Working with Lights and Materials This is the final version of the code that we used to explore lights and materials in Chapter 12, Working with Lights and Materials, on page 109. A1.13 Code: Build Your Own Solar System This is the final version of the solar-system code from Chapter 13, Project: Build Your Own Solar System, on page 117. A1.14 Code: Phases of the Moon This is the final version of the moon-phases code from Chapter 14, Project: Phases of the Moon, on page 125. A1.15 Code: The Purple Fruit Monster Game This is the final version of the game code from Chapter 15, Project: The Purple Fruit Monster Game, on page 133. A1.16 Code: Tilt-a-Board This is the final version of the game code from Chapter 16, Project: Tilt-a- Board, on page 145. A1.17 Code: Learning about JavaScript Objects The code from Chapter 17, Project: Learning about JavaScript Objects, on page 159, should look something like the following. A1.18 Code: Cave Puzzle This is the final version of the game code from Chapter 18, Project: Cave Puzzle, on page 165. A1.19 Code: Multilevel Game This is the final version of the game code from Chapter 19, Project: Multilevel Game, on page 177. A1.20 Code: River Rafting This is the final version of the game code from Chapter 20, Project: River Rafting, on page 185. It is very long. There are a few extras to play around with, as well. Appendix 1. Project Code • 272 report erratum • discuss APPENDIX 2 JavaScript Libraries Used in This Book This appendix contains a list of the JavaScript libraries used in this book, and details on how you can find more information about each. A2.1 Three.js The Three.js JavaScript library is the main library used throughout this book. The home page for the project is http://threejs.org/. The home page includes lots of cool animations and samples, many of which you can try in the ICE Code Editor. We’re using version 52 of Three.js. Detailed documentation for properties and methods not discussed in this book can be found at http://gamingjs.com/docs/ threejs/. A2.2 Physijs The physics engine that is used in this book is Physijs. The home page for the library is http://chandlerprall.github.io/Physijs/. That page includes brief samples and some introductory articles. The Physijs project doesn’t have as much documentation as the Three.js project, but there is some on the project wiki: https://github.com/chandlerprall/Physijs/ wiki. We’re using the version of Physijs that is compatible with Three.js 52. Since Physijs continues to grow, the wiki may refer to newer features than those supported by the version we’re using. A2.3 Tween.js When we want to change values (location, rotation, speed) over the course of time in this book, we use the Tween library. The project home page is http://github.com/sole/tween.js. report erratum • discuss Building a Tween involves several parts. A Tween needs the starting value or values, the ending values, the time that it takes to move from the start to the end values, and a function that’s called as the Tween is running. A Tween also needs to be started and updated to work. The Tween from Chapter 11, Project: Fruit Hunt, on page 99, contains a good example. new TWEEN. Tween({ height: 150, spin: 0 }). to({ height: 250, spin: 4 }, 500). onUpdate(function () { fruit.position.y = this.height; fruit.rotation.z = this.spin; }). start(); This moves between two values: the height and the spin. Over the course of half a second (500 milliseconds), the height moves from 150 to 250. The spin moves from 0 to 4. Each time the Tween is updated, we change the position and rotation of the fruit being animated. The current values being Tweened are made available as a property of the special this object. The last thing we do in the preceding example is to start the Tween. Tweens also need something to tell them to update. In 3D programming, we normally do this in the animate() function with a TWEEN.update() call. function animate() { requestAnimationFrame(animate); TWEEN.update(); renderer.render(scene, camera); } In addition to onUpdate(), there are onStart() and onComplete() methods that call a function when the Tween starts and finishes. A2.4 Scoreboard.js The Scoreboard.js library is a simple JavaScript library that provides the basics of scoring in games. It supports very little configuration, but aims to be easy to use for programmers. Appendix 2. JavaScript Libraries Used in This Book • 274 report erratum • discuss The project home page is https://github.com/eee-c/scoreboard.js. The Scoreboard.js library supports messages, help text, scoring, an elapsed timer, and a countdown timer. Scoreboard Messages Use messages to provide in-game messages to the game player. If you create a scoreboard with var scoreboard = new Scoreboard(), the following methods are available: • scoreboard.message('your message here')—sets the current scoreboard message. This will replace any existing messages. If the message section of the scoreboard is not shown already, this will show it. • scoreboard.addMessage('your message here')—adds more messages to the current scoreboard message. • scoreboard.addMessage('your message here')—adds more messages to the current scoreboard message. • scoreboard.showMessage()—shows the message section of the scoreboard. • scoreboard.hideMessage()—hides the message section of the scoreboard. • scoreboard.clearMessage()—erases the message section of the scoreboard. Help Scoreboard help provides a way to give instructions to the player without cluttering up the message section of the scoreboard. Players need to type a question mark to see the help on the scoreboard. If you create a scoreboard with var scoreboard = new Scoreboard(), the following methods are available: • scoreboard.help('your help instructions here')—sets the scoreboard help. This will replace any existing help. If the help section of the scoreboard is not shown already, this will show it. • scoreboard.showHelp()—shows the help section of the scoreboard. • scoreboard.hideHelp()—hides the help section of the scoreboard. Scoring This feature of the scoreboard keeps track of the number of points the player has earned in the game. report erratum • discuss Scoreboard.js • 275 If you create a scoreboard with var scoreboard = new Scoreboard(), the following methods are available: • scoreboard.score(42)—sets the current score in the game. This will replace any existing score. If no number is supplied, zero is used. If the score section of the scoreboard is not shown already, this will show it. • scoreboard.showScore()—shows the score section of the scoreboard. • scoreboard.hideScore()—hides the score section of the scoreboard. • scoreboard.getScore()—returns the current score in the game. • scoreboard.addPoints(10)—increases the score in the game by the specified number. • scoreboard.subtractPoints(10)—decreases the score in the game by the specified number. Timer This feature keeps track of the total time that has gone by in the game. If you create a scoreboard with var scoreboard = new Scoreboard(), the following methods are available: • scoreboard.timer()—starts the timer in the game. If the timer section of the scoreboard is not shown already, this will show it. • scoreboard.showTimer()—shows the timer section of the scoreboard. • scoreboard.hideTimer()—hides the timer section of the scoreboard. • scoreboard.stopTimer()—stops the timer from counting any more. • scoreboard.startTimer()—starts the timer counting. • scoreboard.resetTimer()—restarts the timer from zero. • scoreboard.getTime()—returns the number of seconds that have elapsed in the game. Countdown This feature keeps track of the total time that has gone by in the game. If you create a scoreboard with var scoreboard = new Scoreboard(), the following methods are available: Appendix 2. JavaScript Libraries Used in This Book • 276 report erratum • discuss • scoreboard.countdown(60)—starts the countdown in the game with the number of seconds supplied. If no time is specified, then 60 seconds will be used. If the countdown section of the scoreboard is not shown already, this will show it. • scoreboard.showCountdown()—shows the countdown section of the scoreboard. • scoreboard.hideCountdown()—hides the countdown section of the scoreboard. • scoreboard.stopCountdown()—stops the countdown from counting any more. • scoreboard.startCountdown()—starts the countdown counting. • scoreboard.resetCountdown(60)—resets the countdown to the specified number of seconds. • scoreboard.getTimeRemaining()—returns the number of seconds left in the game. • scoreboard.onTimeExpired('Time expired message')—sets the message to be shown when time expires. • scoreboard.onTimeExpired(function()) «{ ... }»—if a function is supplied to the onTimeExpired() method, the function will be called when time runs out. A2.5 Sounds.js The Sounds.js JavaScript library contains the bare minimum of sounds for use in games. Full, up-to-date documentation is available at https://github.com/ eee-c/Sounds.js. To use the Sounds.js library, it must be sourced in a At the time of this writing, there were eleven sounds available: bubble, buzz, click, donk, drip, guitar, knock, scratch, snick, spring, and swish. Each sound can be played with code similar to the following: Sounds.bubble.play(); To make a sound repeat, replace the play() method with repeat(): Sounds.bubble.repeat(); To stop the sound at a later time, call the stop() method: Sounds.bubble.stop(); If you want a sound to repeat for a fixed amount of time, then start a repeating sound with a timeout to stop the sound: report erratum • discuss Sounds.js • 277 Sounds.bubble.repeat(); setTimeout(function(){Sounds.bubble.stop();}, 5*1000); The preceding would start repeated bubble sounds. After 5 seconds, the timeout function is run, stopping the repeating bubble sounds. Appendix 2. JavaScript Libraries Used in This Book • 278 report erratum • discuss Index SYMBOLS ! character, 39, 73 () characters errors, 19, 56 grouping math with, 70 * character, multiplication with, 62, 70 + character addition with, 69 joining strings with, 73 - character, subtraction with, 69 . character, method chaining, 84 / character, division with, 70 // notation, commenting with, 69 ; character and functions, 54 errors, 19 = character, defined, 38, 74 == notation, defined, 38, 74 [] characters, listing with, 77 {} characters function notation, 54–55 JavaScript objects, 160 π, 71 DIGITS 2D collisions, 95 orthographic camera and, 91, 167 3D camera perspective and, 7, 90 components, 4 lights and, 187 river-rafting game, 185– 205, 265–272 tilt-a-board game, 145– 157, 249–253 A aboutMe(), 160–164 absolute value, 170 abstractions, 207 acrobatics() function, 61 addAvatar(), 141 addBackground(), 155 addBall(), 148 addBoard(), 150 addControls(), 152 addFruitPowerUp(), 203 addGround(), 136 addLid(), 193 addLights(), 148 addRaft(), 195 addRiver(), 189 addScoreboard(), 138, 188 addSunlight(), 187 addWater(), 193 addition, 69 ambient attribute, 113 ambient color and light about, 113 solar-system project, 118 tilt-a-board game, 148 amplitude, wave, 193 angles and rotation, 81 angular factor, 137, 169 animateFruit(), 106 animations avatar movement, 35–47 cartwheel, 32–33, 39, 44, 61, 66 cave-puzzle game, 165– 184 flipping, 32, 34, 44, 61, 66 fruit in fruit-hunt game, 105 jumping in fruit-hunt game, 104 moving hands and feet, 59–66 pausing, 129–130, 175, 198 phases of the moon, 125– 132 purple fruit monster game, 138 river-rafting game, 185– 205 rotating avatar, 82–84 solar-system project, 119–123 spinning donut, 115 spinning shapes, 14 starting and stopping, 39 tilt-a-board game, 151 walking, 61–66 wiggling in fruit-hunt game, 102 appendChild(), 88 applyCentralForce(), 198 arguments, function, 54, 56 arrow keys avatar movement, 37–40, 46, 140 cartwheels and flips, 66 cave-puzzle game, 169 collision avoidance, 97 if and else keyword con- trols, 76 keycodes, 37 purple fruit monster game, 140 river-rafting game, 197 tilt-a-board game, 145, 152–153 walking animation, 63–65 aspect ratio, 87, 214 assignment operator, 38, 74 assignments, property, 20 asterisk, multiplication with, 62, 70 attributes, 160 avatars camera following, 43–47, 82 cartwheel animation, 32– 33, 39, 44, 61, 66 cave-puzzle game, 168 collision detection, 93– 98, 230–234 creating simple, 25–32, 136, 168, 219 defined, 25 flipping animation, 32, 34, 39, 44, 61, 66 jumping, 103 moving, 35–47, 140, 220– 222 moving hands and feet, 59–66, 223–226 purple fruit monster game, 136–138, 140 rotating, 79–84, 226–229 separating parts, 39 starting and stopping, 97 walking animation, 61–66 axis of rotation, 81 B backgrounds cave-puzzle game, 166 MeshNormalMaterial, 110 solar-system project, 120 starry, 120, 155 tilt-a-board game, 155 backup code, 178 balls, see also spheres shadows, 147, 149 tilt-a-board game, 145– 157, 249–253 beams, tilt-a-board game, 151 blocks, if and while, 75 Blogger, 212 board-tilting game, 145–157, 249–253 board.rotation[dir], 152 bodies, creating, 26–32 tags, using, 85 boilerplate code, 85–91 bonus items, 202–205 Booleans, 39, 73, 96 borders, game, 167 bounciness, 169 boundaries collisions and, 94–98 game, 167 boxes, creating, 6–8 brackets curly, 54, 160 square, 77 breaking things project, 17–24 to learn, 55–56 brightness hexadecimal numbers, 166 specular attribute, 112 browsers, see web browsers buildObstacle(), 181 bunching variables, 122 C cameras adjusting borders for, 168 boilerplate code, 87–91 frame of reference, 127 moving with avatar, 43– 47, 82 orthographic, 89–91, 167 perspective, 7, 90, 168 positioning for games, 134, 147, 186 renderers and, 88–89 resetting, 197 river-rafting game, 197 shadow rendering, 188 solar-system project, 121 switching between, 122, 130 capitalization, 22, 162 cartwheel animation acrobatics() function, 61 camera position, 44 creating, 32–33 keyboard controls, 66 stopping and starting, 39 castShadow attribute, 114 cave puzzle basic game, 165–176, 255–259 multilevel game, 177– 184, 259–265 chaining methods, 84 cheating when programming, 11, see also laziness checkForGameOver(), 200 checkForTreasure(), 104 Chrome, xv, 2 chunkiness, shape, 5–6, 10, 12 circular motion in solar-sys- tem projects, 119, 127 click sound, 183 clock variable, 115 clocks, see timers closing, JavaScript console, 18 code, see also code, project backup, 178 boilerplate, 85–91 hiding, 5 outlining, 135, 186 publishing to web, 207– 215 readability, 50–53 repeating with while, 74 skipping with if, 74, 76 splitting, 179 code editors, see ICE Code Editor code, project avatar movement, 220– 222 cave puzzle, 255–265 collision detection and avoidance, 230–234 creating simple avatars, 219 creating simple shapes, 217 fruit-hunt game, 234–240 lights and materials, 240–241 message-logger function example, 222 movie example of Java- Script objects, 253–255 moving hands and feet, 223–226 phases of the moon, 243– 245 Index • 280 purple fruit monster, 245–249 river rafting, 265–272 rotating avatar, 226–229 solar-system project, 241–243 tilt-a-board game, 249– 253 collisions cave-puzzle game, 169– 170, 182 detecting and avoiding, 93–98, 230–234 goal in tilt-a-board game, 154 multiple in purple fruit monster game, 136– 138 river-rafting game, 204 color ambient, 113, 118 backgrounds, 166 changing, 109–113 emissive attribute, 112 goal lights, 154–155 hexadecimal numbers, 166 list, 43, 110 RGB numbers, 110, 112 solar-system project, 118 specular attribute, 112 trees, 43 color attribute, 113 commenting out, 135 comments double-slash notation, 69 using, 69, 135 while statements, 120 compile-time errors defined, 20 functions, 55 computeFaceNormals(), 193 computeVertexNormals(), 193 cones, creating, 10 console.log(), 160–161 constructors object, 162–164 ramps, 171–174 control keywords, 74–76 controls, see keyboard con- trols; mouse coordinates, converting, 205 copying objects, 161 in programming, 27 projects, 35 this, 180 corners, pulling, 192 cosine curve, 102 JavaScript function, 71– 72 solar-system animation, 119 countdown timers, see al- so timers multilevel games, 181 scoreboard, 100, 174, 181, 276 covers, see materials crescent moon, 131 cubes, creating, 6–8 curly braces function notation, 54–55 JavaScript objects, 160 cylinders, creating, 9–11 D debugging in ICE Code Editor, 19, 86 in JavaScript console, 19–23 decimal points, 204 defining errors, 22 degrees, see radians describing, in JavaScript, 67, 160 Despicable Me, 137 digRiver(), 191 direction avatar, 79–84, 226–229 collision avoidance, 97 directional lights, 187 dirty, defined, 149 __dirtyPosition, 149, 172 __dirtyRotation, 152, 172, 198 discs, creating, 9 distance points, 200
and web hosting, 213 division, 70 documentation, JavaScript, 273–278 domElement property, 88 DOMContentLoaded, 212 Don’t Repeat Yourself (DRY) principle, 51 donuts creating, 12–14 raft from, 195 shadow example, 113 shininess example, 111– 113 spinning, 115 double-slashe notation, 69 draggable ramps, 171–174 DRY principle, 51 E ?e option for ICE Code Editor, 23, 120 Earth phases-of-the-moon project, 127 solar-system project, 119–123 eat(), debugging example, 20 edit-only mode for ICE Code Editor, 23, 120 ?edit-only option for ICE Code Editor, 23, 120 else, 76 else if, 76 emissive attribute, 112 equals character notation, 38, 74 erasing objects, 180 errors compile-time, 20, 55 definition, 22 functions, 55–56, 190 HTML, 86 in ICE Code Editor, 19, 86 in JavaScript console, 20–23 line numbers, 21 quotes, 54, 68 run-time, 20 spelling, 21 event listener avatar movement, 36–40, 46, 140 camera switching, 122, 130 cartwheels and flips, 66 cave-puzzle game, 169– 170, 173, 182 collision detection and avoidance, 97, 136–138 draggable ramps, 173 goal in tilt-a-board game, 154 Index • 281 if and else keyword con- trols, 76 jumping, 103 pausing animations, 130 purple fruit monster game, 136–138, 140 resetting games, 143 river-rafting game, 197 tilt-a-board game, 152– 153 walking animation, 63–65 web-page loading, 211 exclamation point, 39, 73 Explorer, xv, 2 F faces, 191, 193 facing proper direction, 79– 84, 226–229 feet creating, 29–31 moving, 59–66, 223–226 separating from avatar, 39 finish line, river-rafting game, 198–200 Firefox, 2 first-quarter moon, 131 flashGoalLight(), 154 flashing lights, 154 flat surfaces creating, 11 MeshNormalMaterial, 110 warping, 189–193 flipping animation acrobatics() function, 61 camera position, 44 creating, 32, 34 keyboard controls, 66 starting and stopping, 39 forEach(), 77 forest, creating, 40–43 frame of reference, 127–129, 205 free web hosting, 212 freeze-ups, 14, 23, 120 frequency, wave, 193 fruit monster game, 133– 143, 245–249 fruit, power-up, 202–205 fruit-hunt game, 99–107, 234–240 full moon, 131 functions arguments, 54, 56 bunching variables, 122 capitalization, 162 components of, 53 debugging with, 20 errors, 55–56, 190 geometric, 71–72 lists, 77 message-logger example, 49–58, 222 method chaining, 84 null or undefined things, 68 object construction, 162 recursive, 57 reusing code with, 41, 50 skeleton, 190 this keyword and, 180 tree creation, 41 using, 49–58 G game board, tilt-a-board game, 150 game logic, tilt-a-board game, 156 game over check, 138, 200 message, 174 game projects cave puzzle, 165–184, 255–265 fruit-hunt game, 99–107, 234–240 purple fruit monster, 133–143, 245–249 river rafting, 185–205, 265–272 tilt-a-board, 145–157, 249–253 gameOver(), 174 gameStep() cave-puzzle game, 175 pausing and, 199 purple fruit monster game, 139 river-rafting game, 199– 200 scoring and, 200 tilt-a-board game, 156 geometry cubes, 7 cylinders, 9–11 defined, 4 donuts, 12 JavaScript functions, 71– 72 planes and flat surfaces, 11 rays, 94 rotating avatar, 81 spheres, 4 Get WebGL site, xvi getElementById(), 215 gibbous moon, 132 goals, see also scoreboards cave-puzzle game, 169– 170, 182 multilevel games, 182 random placement, 170, 203 river-rafting game, 198– 200 sound effects, 183 tilt-a-board game, 153– 155 Google Chrome, xv, 2 graphics, adding simple, 141 gravity adding, 134, 146 deselecting with 0 con- stant, 151 river-rafting game, 186 gray and specular colors, 112 ground adding, 136 creating for river-rafting game, 190–192 grouping, 32, 47 guitar sound, 183 H hacking, defined, 55 hands creating, 26–29 moving, 59–66, 223–226 hasMoreLevels(), 181 height field mesh, 191 hello() function, 53–58 help messages, scoreboard, 100, 189, 275 hexadecimal numbers, 166 hiding code, 5 HTML about, 85 boilerplate code, 85, 87 markup, 210 tagging when posting games, 213 Hypertext Markup Language, see HTML Index • 282 I ICE Code Editor debugging in, 19, 86 freezing, 14, 23 line numbers for error messages, 22 using, 1–4 id= attribute and web hosting, 213 if skipping code with, 74, 76 tilt-a-board game logic, 156 images, adding simple, 141 immovable objects, see obsta- cles index, list, 204 initializing, 172 Internet, publishing code to, 207–215 Internet Explorer, xv, 2 intersections and rays, 94–98 isActive property, 173 isGoal property, 170 isWalking(), 64 J JavaScript about, xvi, 67 advantages, 78 boilerplate code, 85–91 Booleans, 73 console, 17–24, 160–161 debugging, 19–23 describing things, 67 documentation, 273–278 geometry, 71–72 libraries, 273–278 listing things, 77, 204 mathematics, 69–72 objects, 159–164 shape creation, 4–14 strings, 54, 68, 73 joining strings, 73 jump(), 103 jumping fruit-hunt game, 103 purple fruit monster game, 133–143 K keyboard controls avatar movement, 36–40, 46, 140 camera switching, 122, 130 cartwheels and flips, 66 cave-puzzle game, 169, 173 collision avoidance, 97 if and else keyword con- trols, 76 jumping, 103 pausing animations, 130 purple fruit monster game, 140 resetting games, 143 river-rafting game, 197 tilt-a-board game, 145, 152–153 walking animation, 63–65 keycodes, 37 keywords about, 67 control, 74–76 Kung Fu Panda, 163 L launchFruit(), 139 lazy programming, 27, 129 levelUp(), 181 levels, creating, 177–184, see also multilevel games Levels object, 179–184 lids, 190, 193 lights ambient, 113, 118, 148 directional, 187 flashing, 154 goal, 154–155 point, 118, 148 shadows, 113, 148 shininess and, 111–113 spot, 148 sunlight, 112, 114, 187 tilt-a-board game, 147 using, 109–116, 240–241 line numbers and error mes- sages, 21 linear factor, 137, 169 linear velocity, 138, 197 listenForEvents(), 173 lists collision avoidance, 96 color, 43, 110 multilevel games, 180 removing items from, 204 square brackets, 77 of trees in fruit-hunt game, 101 local coordinates, 205 localToWorld(), 205 log functions example, 49– 58, 222 JavaScript console, 18, 160–161 logic, game, 156 M m_angle, 128 makeBorder(), 167 makeGround(), 190–192 makeTree(), 101 makeTreeAt(), 40–43, 96 markers, avatar, 45–47, 82 Mars phases-of-the-moon project, 126 solar-system project, 119–123 materials bouncy, 169 changing color, 109–113 defined, 4 image, 141 reusing, 27 shininess, 111–113 slippery, 169 using, 109–116, 240–241 Math.abs(), 170 Math.cos(), 72 Math.floor(), 102, 204 Math.PI, 71, 81 -Math.PI/2, 81 Math.random(), 102, 170, 203 Math.sin() about, 62, 72 river creation, 193 walking animation, 62 wiggling animation, 102 mathematics, see also geome- try in JavaScript, 69–72 order of operations, 70 matrix, 205 me variable, 173 Mesh(), 164 mesh changing color, 109–113 construction function, 164 defined, 4 Index • 283 draggable ramps, 173 images, 141 mesh variable, 173 MeshBasicMaterial(), 110 MeshNormalMaterial(), 110, 164 message-logger function exam- ple, 49–58, 222 messages, scoreboard game over, 174 help, 100, 189, 275 methods, 275 method chaining, 84 methods “undefined” error mes- sages, 22 chaining, 84 creating, 164 defined, 161 Microsoft Internet Explorer, xv, 2 moon-phases project, 125– 132, 243–245 mouse controls, cave-puzzle game, 166, 173 Mouse.js, 166, 173 move(), 170, 172 Movie(), 163 movie example of JavaScript objects, 160–164, 253–255 Mozilla Firefox, 2 multilevel games cave puzzle, 177–184, 259–265 river-rafting game, 185– 205, 265–272 multiplication, 62, 70 N names, function errors, 56 negative numbers, 70, 170 new keyword creating scoreboards, 100 object construction, 162– 164 new moon, 131 “no method” error message, 22 normals, recomputing, 193 “not defined” error messages, 22 not operator, 39, 73 not_allowed, 96 null things, 68 numbers decimal points, 204 hexadecimal, 166 in JavaScript, 69 negative, 70, 170 random, 170, 203 RGB color, 110, 112 treated like strings, 73 O object-oriented programming, 159–164, 179 Object.create, 161 objects constructing, 162–164 copying, 161 defined, 160 erasing, 180 immovable, 177, 181 JavaScript, 159–164 Levels, 179 properties and, 179 obstacles cave-puzzle game, 177, 181 multilevel games, 178 on-dom-ready, 211 onComplete(), 274 onStart(), 274 onUpdate(), 274 opacity, goal lights, 154–155 opening, JavaScript console, 18 orbits in solar-system projects, 119, 127 order of operations, 70 orthographic camera, 89–91, 167 outlining code, 135, 186 P parameters, undefined, 155 parentheses errors, 19, 56 grouping math with, 70 particle systems, solar-system project, 120, 155 patterns, 94 pause variable, 129 pausing cave-puzzle game score- board animation, 175 phases of the moon simu- lation, 129–130 river-rafting game, 198 penalizing players, 107 performance chunks and, 6 shadows and, 113 period character, method chaining, 84 perspective cameras 3D and, 7, 90 adjusting game borders for, 168 phases-of-the-moon project, 125–132, 243–245 Phong material, shininess ex- ample, 111–113 physics 0 constant, 151 cave-puzzle game, 166, 169 __dirtyPosition, 149, 172 mouse controls, 166 pausing, 175, 200 purple fruit monster game, 133, 136–138 tilt-a-board game, 146 Physijs, about, 133, 273, see also physics pi, 71 placeGoal() , 171 planes creating, 11 warping to create rivers, 189–193 water in river-rafting game, 193 planets in solar-system project, 119–123 platforms in cave-puzzle game, 182 players, defined, 25, see al- so avatars plus operator addition with, 69 joining strings with, 73 point lights, 118, 148 points, game fruit-hunt game, 103 penalties, 107 power-up, 202–205 river-rafting game, 200– 205 scoreboard methods, 275 points, ray, 96 positioning avatar with arrow keys, 38 Index • 284 borders, 168 camera for games, 134, 147, 186 camera to follow avatar, 43–47, 82 directional lights, 187 feet, 30, 62–63 fruit spinning animation, 106 goal for cave-puzzle game, 170 hands, 28–29, 59, 62–63 ramps for cave-puzzle game, 172 spheres, 6 spinning avatar anima- tion, 82 trees, 42 walking animation, 62–63 power-up points, 202–205 premature generalization, 53, 149 programming cheats, 11 copying in, 27 frustrations, 17 laziness, 27, 129 object-oriented, 159– 164, 179 outlining code, 135, 186 readability, 50–53 project code, see code, project properties, objects and, 179 property assignments, 20 prototypes, 162, 164 publishing code to Web, 207– 215 pulling corners, 192 purple fruit monster game, 89–91, 133–143, 245–249 push onto lists, 96, 101, 180 pushRaft(), 198 puzzle, cave basic game, 165–176, 255–259 multilevel game, 177– 184, 259–265 pyramids, creating, 10 Pythagorean theorem, 104 Q quarter moon, 131 quotes, closing, 54, 68 R radians, 71, 81 raft, creating, 195 rafting game, 76, 185–205, 265–272 ramps, draggable, 171–174 random goals, 170, 203 random numbers, 170, 203 rays and intersections, 94–98 readability, code, 50–53 recursion, 57, 155 red X errors, 19, 86 reference, frame of, 127–129, 205 renderer variable, 214 renderer.render(), 88 renderers boilerplate code, 88–89 defined, 88 pausing animations, 129– 130 shadows, 113 shininess, 111 size and web hosting, 214 switching, 111 repeating code with while, 74 DRY programming princi- ple, 51 sounds, 277 requestAnimationFrame(), 116 resetBall(), 149, 154 resetItems(), 202 resetting ball in tilt-a-board game, 149, 154 bonus items, 202 purple fruit monster game, 138, 143 river-rafting game, 196 retrograde motion, 123 return in functions, 54 RGB numbers, 110, 112 river adding items to, 203 creating, 189–193 river-rafting game, 76, 185– 205, 265–272 river_points property, 203 road for purple fruit monster game, 89–91 rotate() method, 172 rotating π and, 71 animation for shapes, 14 avatars, 79–84, 226–229 camera in solar-system project, 122 cartwheel animation, 32– 33 cylinders and tubes, 9 dirty, 152, 198 flipping animation, 34 fruit spinning animation, 106 game board, 152 phases-of-the-moon project, 128 planes and flat surfaces, 12 ramps for cave-puzzle game, 172–173 river-rafting game, 191, 198 shapes to see 3D, 7 solar-system project, 119 spinning donut, 115 run-time errors, defined, 20 S saving backup code, 178 work in ICE, 4 scene.simulate(), 138 scenes boilerplate code, 87 camera aspect ratio, 87 defined, 4 game-over check, 138 multilevel games, 178, 180 pausing, 200 physics-enabled, 134, 147 scorePoints(), 104 Scoreboard.js, methods, 274– 277, see also scoreboards scoreboards, see also goals cave-puzzle game, 166, 174, 179, 181 fruit-hunt game, 100, 105 help messages, 100, 189 methods, 274–277 multilevel games, 179, 181 pausing, 198 purple fruit monster game, 133, 138 resetting, 197 Index • 285 river-rafting game, 185, 188, 197, 200–205 sound effects, 105