7.2 Simulate collision elastic ball

We used the gravity sensor in the previous section (7.1.0) to control the movement of the ball. At this time, the gravity sensor data is only used for a simple displacement control, you will find It is completely different from the state of motion of the small ball in reality. In this section we improve the code to implement a simple elastic collision ball simulation.

First we need to improve the Ball class so that we can know if it has collided with the Box and which edge it collided after moving it.

The New Ball class code as below:

class Ball:
    limitX = None
    limitY = None
    defX = None
    defY = None
    radius = None
    lastX = None
    lastY = None

    def __init__(self, x, y, r, limitRange = (127, 63) ):
        self.defX = x
        self.defY = y
        self.radius = r

        self.limitX = limitRange[0] - self.radius
        self.limitY = limitRange[1] - self.radius

        self.lastX = x
        self.lastY = y

    def reset(self):
        """
        Move ball to center of screen
        """
        self.lastX = self.defX
        self.lastY = self.defY

    def move(self, offsetX, offsetY):
        """
        Move ball coordinate
        return Boolean, if '0' means the ball not collide side of screen
        return integer, 0x1: top, 0x2: bottom, 0x4: left, 0x8: right
        """
        self.lastX += offsetX
        self.lastY += offsetY

        collideX = 0x0
        if self.lastX < self.radius: 
            self.lastX = self.radius
            collideX = collideX | 0x1

        if self.lastX > self.limitX:
            self.lastX = self.limitX
            collideX = collideX | 0x2

        collideY = 0x0
        if self.lastY < self.radius: 
            self.lastY = self.radius
            collideY = collideY | 0x4

        if self.lastY > self.limitY:
            self.lastY = self.limitY
            collideY = collideY | 0x8

        return collideX | collideY

    def getXY(self):
        """
        return coordinate of ball
        """
        return ( ( self.lastX - self.radius, self.lastY - self.radius ), ( self.lastX + self.radius, self.lastY + self.radius ) )
  • We have improved Ball's move function. When you move the Ball, it can return whether it collides with the Box and which Box edges are colliding.
  • top: 0x1, bottom: 0x2, left: 0x4, right: 0x8, and can collide multiple edges at the same time.
  • For example, return: 6 (0x6) means that both the bottom and the left are collided

The code for collision simulation using gravity sensor data below:

# Get Accel data from attitude sensor
accelData = self.RPiSpark.Attitude.getAccelData()

vX += accelData["x"]
vY += accelData["y"]

collideXY = self.ball.move( -vX, vY )

if collideXY > 0:
    if collideXY in [1,2,5,6,9,10]:
        vX += self.damping if vX < 0 else -self.damping
        vX = -vX

    if collideXY in [4,8,5,6,9,10]:
        vY += self.damping if vY < 0 else -self.damping
        vY = -vY

We open BallBox_7_1.py and add the above code to the run() function to complete the gravity sensing control ball:

from JMRPiFoundations.Skeleton.RPiSparkModule import RPiSparkModule
from Ball import Ball

class BallBox(RPiSparkModule):
    ball = None
    damping = None

    def _drawVel(self):
        self.RPiSpark.Screen.Canvas.rectangle( (2, 2, 38, 16), 0, 0 )
        self.RPiSpark.Screen.write("DAM:{:1d}".format( self.damping ), xy=(0,4))

    def _changeDam(self, offsetVel):
        """
        Change damping of ball move
        offsetVel can be > 0 to increase damping or < 0 to reduce damping
        damping is limited between 0 and 9, default: 3
        """
        self.damping += offsetVel
        if self.damping < 0:
            self.damping = 0

        if self.damping > 9:
            self.damping = 9

    def onKeyButtonUp(self, channel):
        # Press SW_A to reduce damping
        if channel == self.RPiSparkConfig.BUTTON_ACT_A:
           self._changeDam(-1)
           return

        # Press SW_B to increase damping
        if channel == self.RPiSparkConfig.BUTTON_ACT_B:
           self._changeDam(1)
           return

    def setup(self):
        self.damping = 3
        self.ball = Ball(64, 32, 5)
        # setup all key buttons to INT mode, same time query work fine
        self.initKeyButtons("INT")
        # Open accel
        self.RPiSpark.Attitude.openWith(temp=True, accel=True, gyro=False)

    def run(self):

        vX = 0
        vY = 0
        collideXY = 0

        while True:
            # Check exit key status ( JOY_UP + SW_A )
            if self.readExitButtonStatus():
                break;

            # Get Accel data from attitude sensor
            accelData = self.RPiSpark.Attitude.getAccelData()

            vX += accelData["x"]
            vY += accelData["y"]

            collideXY = self.ball.move( -vX, vY )

            if collideXY > 0:
                if collideXY in [1,2,5,6,9,10]:
                    vX += self.damping if vX < 0 else -self.damping
                    vX = -vX

                if collideXY in [4,8,5,6,9,10]:
                    vY += self.damping if vY < 0 else -self.damping
                    vY = -vY

            # Move the ball up
            if self.readKeyButton(self.RPiSparkConfig.BUTTON_JOY_UP):
                self.ball.move(0, -self.damping)

            # Move the ball down
            if self.readKeyButton(self.RPiSparkConfig.BUTTON_JOY_DOWN):
                self.ball.move(0, self.damping)

            # Move the ball left
            if self.readKeyButton(self.RPiSparkConfig.BUTTON_JOY_LEFT):
                self.ball.move(-self.damping, 0)

            # Move the ball right
            if self.readKeyButton(self.RPiSparkConfig.BUTTON_JOY_RIGHT):
                self.ball.move(self.damping, 0)

            # Move the ball to center of screen
            if self.readKeyButton(self.RPiSparkConfig.BUTTON_JOY_OK):
                self.ball.reset()
                vX = 0
                vY = 0

            # Drawing the ball on the screen
            self.RPiSpark.Screen.clear()
            self._drawVel()
            self.RPiSpark.Screen.Canvas.ellipse( self.ball.getXY(), 1, 1 )
            self.RPiSpark.Screen.refresh()

        self.releaseKeyButtons()
        print("BallBox is done.")

Save as BallBox_7_2.py and execute the following command in the terminal:

$> rspk BallBox_7_2 -f

At this time, you can control the movement of the ball by changing the attitude of RPi-Spark and you will find some situations that are close to the actual elastic collision.


 

Was this article helpful?

YES     |     NO

 

Enjoying the project? Spotted a mistake? Any opinions on the website? Let us know!