• Tidak ada hasil yang ditemukan

FIGURE 2.13 Frames from an animation of a triangle shifing color.

If desired, you could even make the triangle’s color shif along a greater range of colors by also modifying the green and blue components of the color variable. In this case, the sine waves will also need to be shifed, so that each component reaches its peak value at diferent times. Mathematically, this can be accomplished by adding values to the t variable within the sine function. Tis can be accomplished, for example, by replacing the code added above with the following:

self.baseColor.data[0] = (sin(self.time) + 1) / 2 self.baseColor.data[1] = (sin(self.time + 2.1) + 1) / 2 self.baseColor.data[2] = (sin(self.time + 4.2) + 1) / 2 Feel free to experiment with and combine these diferent animations to see what you can produce!

Keys that are designated as down or up should only remain so for a single iteration of the main application loop, and so the contents of these two lists must be cleared before checking the Pygame event list for new events during the next iteration.

An event or action is said to be discrete if it happens once at an isolated point in time, or continuous if it continues happening during an interval of time. Te keydown and keyup events are discrete, but many applica- tions also feature continuous actions (such as moving an object on screen) that occur for as long as a key is being held down. Here, the term pressed will be used to refer to the state of a key between the keydown and keyup events (although it should be noted that there is no standard terminology for these three states). You will keep track of the names of pressed keys in a third list. Finally, for readability, the contents of each list can be queried using functions you will add to the Input class.

To make the modifcations, open the input.py fle in the core folder.

In the Input class _ _ init _ _ function, add the following code:

# lists to store key states

# down, up: discrete event; lasts for one iteration

# pressed: continuous event, between down and up events

self.keyDownList = []

self.keyPressedList  = []

self.keyUpList = []

Next, in the update function, add the following code before the for loop:

# reset discrete key states self.keyDownList = []

self.keyUpList = []

Within the for loop, add the following code:

# check for keydown and keyup events; 

# get name of key from event

# and append to or remove from corresponding lists if event.type == pygame.KEYDOWN:

 keyName = pygame.key.name( event.key )  self.keyDownList.append( keyName )  self.keyPressedList.append( keyName ) if event.type == pygame.KEYUP:

    

                 

      

 keyName = pygame.key.name( event.key )  self.keyPressedList.remove( keyName )  self.keyUpList.append( keyName )

Finally, add the following three functions to the Input class:

# functions to check key states def isKeyDown(self, keyCode):

 return keyCode in self.keyDownList  def isKeyPressed(self, keyCode):

 return keyCode in self.keyPressedList def isKeyUp(self, keyCode):

 return keyCode in self.keyUpList

As previously indicated, you will now create a text-based application to verify that these modifcations work as expected, and to illustrate how the class will be used in practice. In your main folder, create a new fle named test-2–10.py containing the following code:

from core.base import Base

# check input class Test(Base):

 def initialize(self):

 print("Initializing program...")  def update(self):

 # debug printing

 if len(self.input.keyDownList) > 0:

      print( "Keys down:", self.input.

keyDownList )

 if len(self.input.keyPressedList) > 0:

      print( "Keys pressed:", self.input.

keyPressedList )

 if len(self.input.keyUpList) > 0:

 print( "Keys up:", self.input.keyUpList )

# instantiate this class and run the program Test().run()

When you run this program, pressing any keys on the keyboard should cause messages to be printed that show the contents of the non-empty key lists: the names of any keys that are down, pressed, or up. Afer verifying that this code works as expected, either comment out or delete the code in the application’s update function and replace it with the following code, which contains examples of how the functions are typically used.

# typical usage

if self.input.isKeyDown("space"):

 print( "The 'space' key was just pressed down.") if self.input.isKeyPressed("right"):

    print( "The 'right' key is currently being pressed.")

When you run this program, pressing the space bar key should cause a single message to appear each time it is pressed, regardless of how long it is held down. In contrast, pressing the right arrow key should cause a series of messages to continue to appear until the key is released. It is possible that on diferent operating systems, diferent names or symbols may be used for particular keys (such as the arrow keys); this can be investigated by running the application with the previous code.

2.5.2 Incorporating with Graphics Programs

Now that the framework is able to process discrete and continuous key- board input, you will create the graphics-based application described ear- lier that enables the user to move a triangle using the arrow keys. As this application is similar to many of the animation examples previously dis- cussed, begin by making a copy of the fle test-2–7.py from the main folder and name the copy test-2–11.py. In this new fle, at the end of the initialize function, add the following code:

# triangle speed, units per second self.speed = 0.5

Te speed variable specifes how quickly the triangle will move across the screen. Recalling that the horizontal or x-axis values displayed on screen range from −1 to 1, a speed of 0.5 indicates that the triangle will be able to move fully across the screen in 4 seconds.

Next, in the update function, delete the code between the comments

### update data ### and ### render scene ###, and in its place, add the following code:

distance = self.speed * self.deltaTime if self.input.isKeyPressed("left"):

 self.translation.data[0] -= distance if self.input.isKeyPressed("right"):

 self.translation.data[0] += distance if self.input.isKeyPressed("down"):

 self.translation.data[1] -= distance if self.input.isKeyPressed("up"):

 self.translation.data[1] += distance

Depending on your operating system, you may need to change the strings checked by the isKeyPressed function; for maximum cross-platform compatibility, you may want to use the frequently used set of letters w/a/s/d in place of up/lef/down/right, respectively.

Note that this segment of code begins by calculating how far the triangle should be moved across the screen (the distance traveled), taking into account the elapsed time since the previous render, which is stored in the variable deltaTime that you added to the Base class in Section 2.4.3.

Te calculation itself is based on the physics formula speed = distance/time, which is equivalent to distance = speed * time. Also note that a sequence of if statements are used, rather than if-else statements, which allows the user to press multiple keys simultaneously and move in diagonal direc- tions, or even press keys indicating opposite directions, whose efects will cancel each other out. You may have noticed that translations of the z component (the forward/backward direction) were not included; this is because such a movement will not change the appearance of the shape on screen, since perspective transformations have not yet been introduced into the framework.

Once you have fnished adding this code, run the application and try pressing the arrow keys to move the triangle around the screen, and con- gratulations on creating your frst interactive GPU-based graphics program! 2.6 SUMMARY AND NEXT STEPS

In this chapter, you learned how to create animations and interactive applications. Each of these examples involved polygon shapes (the z-coordinate of each point was always set to zero), and the movement was

limited to translations (of the x and y coordinates). A natural next goal is to transform these shapes in more advanced ways, such as combining rotations with translations. Perspective transformations also need to be introduced so that translations in the z direction and rotations around the x-axis and y-axis of the scene will appear as expected. In the next chapter, you will learn the mathematical foundations required to create these trans- formations, and in the process, create truly three-dimensional scenes.

C H A P T E R

3

Matrix Algebra and Transformations

I

n this chapter, you will learn about some mathematical objects—vectors and matrices—that are essential to rendering three-dimensional scenes.

Afer learning some theoretical background, you will apply this knowledge to create a Matrix class that will be fundamental in manipulating the position and orientation of geometric objects. Finally, you will learn how to incorpo- rate matrix objects into the rendering of an interactive 3D scene.