OpusScript Tutorial:

Bouncing a Ball

Part 3: Giving the User Control

Grabbing the Ball in Motion

We have so far determined how to make a vector object fall as though affected by gravity and have successfully introduced horizontal motion into this model. We have also enabled the object to bounce realistically against the walls and floor of the publication, gradually losing momentum until coming to rest.

However, the initial horizontal and vertical speeds of the ball are currently defined by two constants, 'speedX' and 'speedY'. This means that every time the publication is previewed, the ball will bounce in exactly the same fashion, bouncing at exactly the same points around the publication and coming to rest in exactly the same position as before. Wouldn't the publication be much more fun if we could grab the ball in mid-air, drag it around the screen and throw it in a direction of our choosing?

The first thing we need to do is implement a way of detecting whether the ball is being clicked on. The simplest way to do this is to add a Left Mouse Down trigger to the ball, followed by a Set Variable action which changes the value of a variable. We use a Left Mouse Down trigger as this detects when the left mouse button is depressed, whereas a Left Mouse Click trigger only acts when the left mouse button is released.

If the Script Object window is still visible, please minimise or close this to display the page with the Ball object. Now right-click on the Ball object and select Edit Actions. Please select the Triggers tab, then open the Mouse group of actions. You should now be able to see the Left Mouse Down trigger shown below:

Please double-click on the Left Mouse Down trigger and this trigger should appear in the left-hand list:

Now click on the Programming tab and open the Variables group of actions. You should now be able to see the Set Variable action shown below:

Double-click on the Set Variable action. The left-hand list should now look like this:

You should also see the following options on the right-hand side:

Please click on the New button to create a new variable. Name this new variable pressed and give it a default numerical value of zero. The New Variable dialog should now look like the following:

Click OK to confirm the changes and return to the Set Variable settings. Now change the Number value to 1.00 and set it to Overwrite existing contents. The dialog should resemble the following:

Click Apply to confirm the changes. The left-hand list should appear as follows:

All being well, please click OK to return to the editor.

So, clicking on the ball changes the value of a variable, but this currently has no effect on the ball's movement. We need to stop the ball in its tracks when this variable changes, then make it follow the cursor around the screen. To do this, we need to go back into our Script Object which should currently look like the following:

floorX1 = 25
floorX2 = 775
floorY1 = 25
floorY2 = 575
speedX = 10
speedY = 0
gravity = 1
bounce = 0.9
while (true) {
  speedY = speedY + gravity
  Ball.Move(speedX,speedY)
  pos = Ball.GetPosition()
  if (pos.x < floorX1) {
    Ball.SetPositionX(floorX1)
    speedX = speedX * -bounce
  }
  if (pos.x > floorX2) {
    Ball.SetPositionX(floorX2)
    speedX = speedX * -bounce
  }
  if (pos.y < floorY1) {
    Ball.SetPositionY(floorY1)
    speedY = speedY * -bounce
  }
  if (pos.y > floorY2) {
    Ball.SetPositionY(floorY2)
    speedX = speedX * bounce
    speedY = speedY * -bounce
  }
  wait(0.01)
}

We need to add a condition to our set of looped actions which will check the current value of the 'pressed' variable. If 'pressed' is equal to 1 (i.e; the ball has been grabbed), we want the ball to ignore the existing actions and simply follow the cursor. However, if 'pressed' is equal to zero (the default value) we want gravity to take over again and allow the ball to bounce around the screen as normal. Let's try the following:

floorX1 = 25
floorX2 = 775
floorY1 = 25
floorY2 = 575
speedX = 10
speedY = 0
gravity = 1
bounce = 0.9
while (true) {
  if (pressed == 1) {
  } else {
    speedY = speedY + gravity
    Ball.Move(speedX,speedY)
    pos = Ball.GetPosition()
    if (pos.x < floorX1) {
      Ball.SetPositionX(floorX1)
      speedX = speedX * -bounce
    }
    if (pos.x > floorX2) {
      Ball.SetPositionX(floorX2)
      speedX = speedX * -bounce
    }
    if (pos.y < floorY1) {
      Ball.SetPositionY(floorY1)
      speedY = speedY * -bounce
    }
    if (pos.y > floorY2) {
      Ball.SetPositionY(floorY2)
      speedX = speedX * bounce
      speedY = speedY * -bounce
    }
  }
  wait(0.01)
}

Now preview the publication. The ball should bounce around the screen as normal, but if you click on the ball (if you can catch it!) the ball should stop dead in its tracks. This is because we have set no actions to happen when 'pressed' is equal to 1 and actions only occur after the 'else' condition (i.e; when 'pressed' is not equal to 1).

Dragging the Ball Around the Screen

We can now stop the ball from moving, but what do we want to do with it once it is under our control? We need to find a way of making the ball follow the cursor around the screen. This is actually quite easy to achieve using a GetMousePosition() action to determine the current position of the mouse cursor, followed by a SetPosition() action to set the ball to the position of the cursor. Let's add these actions into the if (pressed == 1) condition:

floorX1 = 25
floorX2 = 775
floorY1 = 25
floorY2 = 575
speedX = 10
speedY = 0
gravity = 1
bounce = 0.9
while (true) {
  if (pressed == 1) {
    mouse = GetMousePosition()
    Ball.SetPosition(mouse.x,mouse.y)
  } else {
    speedY = speedY + gravity
    Ball.Move(speedX,speedY)
    pos = Ball.GetPosition()
    if (pos.x < floorX1) {
      Ball.SetPositionX(floorX1)
      speedX = speedX * -bounce
    }
    if (pos.x > floorX2) {
      Ball.SetPositionX(floorX2)
      speedX = speedX * -bounce
    }
    if (pos.y < floorY1) {
      Ball.SetPositionY(floorY1)
      speedY = speedY * -bounce
    }
    if (pos.y > floorY2) {
      Ball.SetPositionY(floorY2)
      speedX = speedX * bounce
      speedY = speedY * -bounce
    }
  }
  wait(0.01)
}

Now preview the publication. When you click on the ball, it should now follow the cursor around the screen.

Releasing the ball

One problem with the current publication is that, once the ball has been grabbed, there is no way of releasing it. This is because the 'pressed' variable never gets set back to zero. We therefore need a way of resetting this variable back to its default state once we release the ball from our grip.

The easiest way to do this is to add a Left Mouse Click trigger to the page itself, so that, wherever the ball is currently positioned on the page, releasing the left mouse button resets the 'pressed' variable and the ball once again moves according to our original physics engine.

Again, minimise the Script Object to display the page, then right-click on the page and select Edit Actions. Now select the Triggers tab, open the Mouse group of triggers and you should be able to see the Left Mouse Click trigger, as shown below:

Double-click on the Left Mouse Click action to apply it to the left-hand list:

Now click on the Programming tab and open the Variables group of actions. You should now be able to see the Set Variable action shown below:

Double-click on the Set Variable action. The left-hand list should now look like this:

You should also see the following options on the right-hand side:

Please open up the Variable drop-down menu and select the 'pressed' variable we created earlier. Now enable the Number option and set the value to 0.00. Please also ensure that the Overwrite current contents option is enabled. The dialog box should now look like the following:

Click Apply to confirm the changes. The left-hand list should appear as follows:

All being well, please click OK to return to the editor.

Now preview the publication. You should now be able to grab the ball in mid-air, drag it to a position of your choice and release the ball. The ball will then continue moving with the direction and speed it possessed before you grabbed it.

Throwing the ball

In real life, we would not expect a ball to remember its speed and direction after being grabbed and released. In fact, the ball's horizontal and vertical speed should reset to zero once grabbed, so that if the ball is released (without being thrown), it drops straight down to the ground.

But what happens if a ball is thrown? How do we determine what speed the ball should leave our grasp? We need to constantly monitor how fast the ball is being dragged around the screen, then transfer this speed to the ball if it is released, or recalculate the speed if it is not.

To determine how fast the ball is being dragged, we need to determine the ball's initial position, then determine the position of the cursor. As the ball is currently following the cursor, you might expect these positions to be exactly the same. However, as our script actions all run sequentially (rather than simultaneously), it is possible for the cursor to have moved whilst the position of the ball was being calculated.

The difference between the ball's initial position and the current position of the cursor will give us the total distance the cursor has travelled in a fixed period of time or, in other words, its speed (calculated by the equation distance/time).

We can then set the horizontal and vertical speeds of the ball to the horizontal and vertical speeds of the cursor. Should the ball then be released, it will leave our grasp with these speeds. If the ball is not released, the whole sequence of actions are repeated and the cursor speed is recalculated.

Let's see how this would look in code. The first thing we need to do is determine the ball's current position when the ball is grabbed (pressed == 1):

if (pressed == 1) {
  pos = Ball.GetPosition()
  mouse = GetMousePosition()
  Ball.SetPosition(mouse.x,mouse.y)
}

To detemine the distance the ball has moved during these actions, we simply need to subtract the position of the ball (the initial position) from the position of the mouse cursor (the current position). This will leave us with the total distance travelled which we can set as the ball's speed. Please note that we have to do this for the horizontal and vertical speeds. Let's take a look at the code:

if (pressed == 1) {
  pos = Ball.GetPosition()
  mouse = GetMousePosition()
  Ball.SetPosition(mouse.x,mouse.y)
  speedX = mouse.x - pos.x
  speedY = mouse.y - pos.y
}

The entire Script Object code should now read:

floorX1 = 25
floorX2 = 775
floorY1 = 25
floorY2 = 575
speedX = 10
speedY = 0
gravity = 1
bounce = 0.9
while (true) {
  if (pressed == 1) {
    pos = Ball.GetPosition()
    mouse = GetMousePosition()
    Ball.SetPosition(mouse.x,mouse.y)
    speedX = mouse.x - pos.x
    speedY = mouse.y - pos.y
  } else {
    speedY = speedY + gravity
    Ball.Move(speedX,speedY)
    pos = Ball.GetPosition()
    if (pos.x < floorX1) {
      Ball.SetPositionX(floorX1)
      speedX = speedX * -bounce
    }
    if (pos.x > floorX2) {
      Ball.SetPositionX(floorX2)
      speedX = speedX * -bounce
    }
    if (pos.y < floorY1) {
      Ball.SetPositionY(floorY1)
      speedY = speedY * -bounce
    }
    if (pos.y > floorY2) {
      Ball.SetPositionY(floorY2)
      speedX = speedX * bounce
      speedY = speedY * -bounce
    }
  }
  wait(0.01)
}

Now preview your publication. Clicking on the ball should stop it dead and you should still be able to drag it around the screen. However, if you quickly drag the ball across the screen, then release the left-mouse button, the ball should fly off with the direction and speed of the cursor. You can now throw the ball in the direction of your choice and finally get to test out the ceiling of the publication!

In the final part of this tutorial, we will look at ways in which we can improve the appearance of the publication, adding depth through the use of shadows and lighting.

tutorial to create a bouncing ball simulation in Opus Pro