Simple Gaming
Top  Previous  Next


Description
This is a step-by-step tutorial using the On Frame Event to show how Objects can be animated to provide simple gaming functionality.

Aim
This tutorial uses the onEnterFrame() Event to animate Objects.

.swi file
"game.swi"

1.Open the "dragging.swi" file used in the Mouse Dragging tutorial and save the file as "mygame.swi"  

2.Draw a rectangle to define the playing area. Name the rectangle "boundary" and tick the 'Target' checkbox  

scripttute10_1


3.Rename the ball Object "bat" and change its Event scripts to be the following:  

onSelfEvent (press) {
   // Toggle drag mode on / off on mouse press.
   if (blDragMode) {
      stopDrag();
      blDragMode = false;
   }
 else {
      startDragLocked();
      blDragMode = true;
   }

}
onLoad () {
   // blDragMode is used to define if object is currently being dragged.
   blDragMode = false;
   //
   // x0, y0 are used to store the position of the object in the previous frame.
   x0 = _X;
   y0 = _Y;
   //
   // dx, dy are the x and y velocity of the object.
   // ie. dx is the number of pixels in the x direction that the object moved since the previous frame.
   dx = 0;
   dy = 0;
}

onEnterFrame() {
   // Calculate and save the distance moved since the last frame. This is the velocity.
   dx = _X - x0;
   dy = _Y - y0;
   // Save the current position for calculation in the next frame
   x0 = _X;
   y0 = _Y;
}


As the scripting is becoming more complex, it is useful to add comments using Add Script | Debugging | Comments. Comments are shown as a line starting with // or surrounded by /* */.  

The onLoad() function is used to declare variables and setup initial values.  
The onSelfEvent(press) is used to toggle the dragging of the bat Object. Press the left mouse button once on the 'bat' object to start dragging. Press again to stop dragging. Note that the original onSelfEvent(release) has been deleted.  
The onEnterFrame() function is called at the start of each Frame. This function is used to calculate and store the instantaneous speed of the bat. The speed is calculated by saving the number of pixels that the Object has moved since the function was previously called. The current position is then saved in x0, y0 for use when the function is called again on the next Frame.  
 
Note that in the image below, only a subsection of the script is shown.  

scripttute10_2



4.Use the Circle Tool to create a new circle. Colour the circle blue. The diameter of this circle was 40  

scripttute10_3

Note: If you create the circle by copying the bat Object, be sure to remove any script associated with the original Object


5.Use the Menu function Modify | Grouping | Group as Sprite to change the unnamed shape into a Sprite. Name the sprite "ball". Using a Sprite for the ball allows multiple balls to be added later using Instances  

scripttute10_4


6.Add the following script to the 'ball' Sprite. The script below contains some hyperlinks to assist with understanding.  

onLoad () {
   // Define variables and initial conditions.
   // dx is the pixels the object moves per frame in the x direction.
   // dy is the pixels the object moves per frame in the y direction.
   //
   // MinX, MaxX, MinY and MaxY are the minimum and maximum allowable positions for the object.
   // These are calculated based on the size of the boundary object and the radius of this shape.
   dx = 0;
   dy = 0;
   MinX = _root.boundary._X - _root.boundary.
_width / 2 + this._width / 2;
   MaxX = _root.boundary._X + _root.boundary._width / 2 - this._width / 2;
   MinY = _root.boundary._Y - _root.boundary.
_height / 2 + this._width / 2;
   MaxY = _root.boundary._Y + _root.boundary._height / 2 - this._width / 2;
   // batradius is the combined radius of the bat and the ball
   batradius = this._width / 2 + _root.bat._width / 2;
}
onEnterFrame() {
   // Move ball to new position
   _X
+= dx;
   _Y += dy;
   //
   // Check for boundary collision. If collision detected, reverse velocity
   // Use of Math.abs is more robust than simply -dx, -dy.
   // This will work correctly if dx or dy > R but < 2 * R and ball straddles boundary.
   
if (_X < MinX) {
      // Force dx to be positive.
      dx =
Math.abs(dx);
   }
   if (_X
> MaxX) {
      // Force dx to be negative.
      dx = -Math.abs(dx);
   }
   if (_Y < MinY) {
      // Force dy to be positive.
      dy = Math.abs(dy);
   }
   if (_Y > MaxY) {
      // Force dy to be negative.
      dy = -Math.abs(dy);
   }
   //
   // Check for collision with bat.
   if (_parent.bat.
isNearThis(batradius)) {
      // We have collided with bat, change ball velocity.
      dx = CalcNewDx();
      dy = CalcNewDy();
   }
}
function CalcNewDx() {
   // Function to calculate the new x velocity of the ball
   // This is not correct physics, (no bounce). Just take on the velocity of the bat
   
return _root.bat.dx;
}
function CalcNewDy() {
   // Function to calculate the new y velocity of the ball
   // This is not correct physics, (no bounce). Just take on the velocity of the bat
   return _root.bat.dy;
}

Again, many comments have been used to describe the script. To save time they can be omitted, but they do help explain the code.  
 
For this Object the onLoad() function is used to declare and initialise variables. dx and dy are used to hold the Object's velocity. These are initialised to 0.  
 
MinX, MinY, MaxX and MaxY are used to hold the boundary area that the ball is allowed to travel in. These points are based on the size of the boundary Object and are corrected to take into account the radius of the ball.  
 
Batradius is used to hold the combined radius of the bat and the ball. This is calculated from bat._width / 2 + ball._width / 2. This radius is used when checking a collision condition. A collision has occurred when the two Objects are within Batradius of each other (ie. touching or overlapped).  
 
The CalcNewDx() and CalcNewDy() functions can be entered via Add Script | Define function (...) (Leave all parameter fields blank). The functions are used to calculate the new x and y velocity of the ball after a collision with the bat. Functions are used here as they provide a way of grouping sections of code. This can be used to help keep each module small enough to be easily understood. For maximum readability, no function should extend beyond 1 page of display. Initially, the functions do not calculate the physics associated with the ball bouncing off the bat. The bat is assumed to be a 'sticky' bat, i.e. the ball simply adopts the current velocity of the bat.  

The onEnterFrame() Event function is called on each Frame. It performs the following tasks:  
·updates current ball position based on current dx, dy values  
·checks for collision with boundary. Reverses dx, dy if collision is detected  
·checks for collision with bat. Updates dx, dy according to values returned from CalcNewDx() and CalcNewDy() functions.  

Note: When analysing the script,
a += b; means a = a + b;.


7.Test the game by selecting the 'Layout' tab and pressing the 'Play' button. The balls should be stationary. Click on the red ball (bat object) with the mouse. The bat will now follow the mouse pointer until you click the mouse again. Use the bat to hit the blue ball. The blue ball should continue to bounce around the court until it meets the bat again  

8.Press the 'Stop' button. Select the ball Sprite in the 'Layout' Panel and right click. Select Make Instance. If the new instance appears off the page, drag it back into the playing area. Press the 'Play' button. There are now two ball Objects that can be hit around the court. Note that the Sprite and the Instance behave independently of each other as each of their variables and associated properties (dx, dy, _X, _Y etc.) are local to the individual Object  

Analysis
The onEnterFrame() Event Handling function can be used to animate Objects by altering their _X and _Y properties on a Frame-by-Frame basis.

The onLoad() Event Handling function is useful for defining and initialising variables. If the variables x0 and y0 were defined and initialised within the onEnterFrame() function, their previously stored values would be lost when the function is called on the following Frame.

User defined functions (e.g. CalcNewDx() and CalcNewDy()) can be used to group sections of code and improve readability.

An Instance of a Sprite can behave independently (in a scripting sense) of the original Sprite and is a useful way of creating new game Objects.