Share My Creation LittleSquare Physics Demo

wonder

Expert
Licensed User
Here's a little physics demo I wrote to kill some time at work... Enjoy. :)
Open the attached JAR file and/or download the source code.



PhysiX.b4j
B4X:
#Region  Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 400
#End Region

Sub Process_Globals
    'Custom Types
        Type PhysicalObject2D(posX As Double, posY As Double, sizeX As Double, sizeY As Double, Velocity As Vector, Gravity As Vector)
        Type Line2D(aX As Double, aY As Double, bX As Double, bY As Double, Active As Boolean)
        Type PerformanceMeasurement(Interval As Double, TimeStamp As Long)

    'MainForm
        Private fx                             As JFX
        Private MainForm                       As Form
        Dim cvs As Canvas

    'Main Cycle
        Dim DeltaTime                          As PerformanceMeasurement
        Dim MainCycle                          As Timer

    'Physical Elements
        Dim LittleSquare                       As PhysicalObject2D
        Dim Platform                           As PhysicalObject2D
        Dim World                              As PhysicalObject2D

    'Temporary Variables
        Dim tempVelocity                       As Vector
        Dim tempGravity                        As Vector

    'Constants
        Dim EarthMass = 5.972 * Power(10, 24)  As Double

    'User Input
        Dim LaunchVisual                       As Line2D
        Dim UserInput                          As Pane
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    'MainForm Settings
        MainForm           =  Form1
        MainForm.Resizable =  False
        MainForm.Title     =  "LittleSquare Physics Demo - Bruno Silva 2015"
        MainForm.SetFormStyle("UNIFIED")
        MainForm.Show

    'Canvas
        cvs.Initialize("cvs")
        MainForm.RootPane.AddNode(cvs, 0, 0, MainForm.Width, MainForm.Height)

    'User Input
        UserInput.Initialize("UserInput")
        MainForm.RootPane.AddNode(UserInput, 0, 0, MainForm.Width, MainForm.Height)

    'World Gravity
        tempGravity.setAngleAndMagnitudeD(90, 1600)
        World.Gravity = tempGravity

    'Player
        LittleSquare.sizeX = 15
        LittleSquare.sizeY = 15
        LittleSquare.posX  = Rnd(0, cvs.Width - LittleSquare.sizeX)
        LittleSquare.posY  = 0

    'Player Velocity
        tempVelocity.setAngleAndMagnitudeD(Rnd(0,360), 300)
        LittleSquare.Velocity = tempVelocity

    'Stage
        Platform.sizeX = cvs.Width
        Platform.sizeY = 100
        Platform.posX  = 0
        Platform.posY  = cvs.Height - Platform.sizeY

    'Main Cycle
        DeltaTime.Interval = (1 / 60)
        MainCycle.Initialize("MainCycle", 16) 'DeltaTime.Interval * 1000)
        MainCycle.Enabled = True
End Sub

Sub MainCycle_Tick
    'Calculate DeltaTime Interval
        If Not(DeltaTime.TimeStamp = 0) Then DeltaTime.Interval = (Max(1, DateTime.Now - DeltaTime.TimeStamp) / 1000)
        DeltaTime.TimeStamp = DateTime.Now

    'Clear Screen
        cvs.ClearRect(0, 0, cvs.Width, cvs.Height)

    'Apply Gravity Physics
        tempVelocity.dirY  = LittleSquare.Velocity.dirY + (World.Gravity.dirY * DeltaTime.Interval)
        LittleSquare.Velocity = tempVelocity

    'Collision Detection: Platform
        If LittleSquare.posY + LittleSquare.sizeY >= Platform.posY Then  
            If LittleSquare.posY + LittleSquare.sizeY > Platform.posY And LittleSquare.Velocity.AngleD <= 180 Then
                tempVelocity.dirY  = PostCollisionVelocity(LittleSquare.Velocity.dirY, 0, 10, EarthMass, 0.6)
                LittleSquare.Velocity = tempVelocity
            End If
            If Abs(LittleSquare.Velocity.dirY) < Abs(0.05 * World.Gravity.dirY) Then
                tempVelocity.dirY = 0
                LittleSquare.Velocity = tempVelocity
            End If
            LittleSquare.posY = Platform.posY - LittleSquare.sizeY
        End If

    'Collision Detection: Top
        If LittleSquare.posY < 0 Then
            LittleSquare.posY = 0
            tempVelocity.dirY  = PostCollisionVelocity(LittleSquare.Velocity.dirY, 0, 10, EarthMass, 0.6)
            LittleSquare.Velocity = tempVelocity
        End If

    'Collision Detection: Walls
        If LittleSquare.posX + LittleSquare.sizeX > cvs.Width Then
            LittleSquare.posX = cvs.Width - LittleSquare.sizeX
            tempVelocity.dirX  = PostCollisionVelocity(LittleSquare.Velocity.dirX, 0, 10, EarthMass, 0.6)
            LittleSquare.Velocity = tempVelocity
        Else If LittleSquare.posX < 0 Then
            LittleSquare.posX = 0
            tempVelocity.dirX  = PostCollisionVelocity(LittleSquare.Velocity.dirX, 0, 10, EarthMass, 0.6)
            LittleSquare.Velocity = tempVelocity
        End If

    'Friction
        If LittleSquare.posY + LittleSquare.sizeY >= Platform.posY Then
            tempVelocity.dirX  = LittleSquare.Velocity.dirX * 0.8
            LittleSquare.Velocity = tempVelocity
        End If

    'Backup Coords
        Dim oX = LittleSquare.posX As Double
        Dim oY = LittleSquare.posY As Double

    'Update Position
        LittleSquare.posX = LittleSquare.posX + (LittleSquare.Velocity.dirX * DeltaTime.Interval)
        LittleSquare.posY = LittleSquare.posY + (LittleSquare.Velocity.dirY * DeltaTime.Interval)

    'Calculate the Velocity Angle
        tempVelocity.AngleD = CalcAngleD(oX, oY, LittleSquare.posX, LittleSquare.posY)
        LittleSquare.Velocity  = tempVelocity

    'Render on Screen
        DrawShape(LittleSquare, fx.Colors.Blue)
        DrawShape(Platform, fx.Colors.Green)
        If LaunchVisual.Active Then
            LaunchVisual.aX = LittleSquare.posX + (LittleSquare.sizeX / 2)
            LaunchVisual.aY = LittleSquare.posY + (LittleSquare.sizeY / 2)
            DrawLine(LaunchVisual, fx.Colors.Red, 2)
        End If

    'Debug
        'Log(DeltaTime.Interval)
End Sub

'Given the Conservation of Momentum formula (m1*u1)+(m2*u2) = (m1*v1)+(m2*v2),
'we're able to calculate an object's post collision velocity.
'
'u1: ObjectA pre-collision velocity, u2: ObjectB pre-collision velocity, m1: ObjectA mass in kg, m2: ObjectB mass in kg.
'CoR: Coefficient of Restitution, a value between 0.00(0) and 1.00(0) where 1 represents a perfectly elastic collision.
Sub PostCollisionVelocity(u1 As Double, u2 As Double, m1 As Double, m2 As Double, CoR As Double) As Double
    Dim v1 = ((u1 * (m1 - m2)) + (2 * m2 * u2)) / (m1 + m2) As Double
    Return v1 * CoR
End Sub

Sub Userinput_MousePressed (EventData As MouseEvent)
    Dim dist, aX, aY, bX, bY As Double
    aX    = (LittleSquare.posX + LittleSquare.sizeX / 2)
    aY    = (LittleSquare.posY + LittleSquare.sizeY / 2)
    bX    = (EventData.X)
    bY    = (EventData.Y)
    dist  = Sqrt(Power((bX-aX),2) + Power((bY-aY),2))
    If dist <= LittleSquare.sizeX Then
        LaunchVisual.aX     = aX
        LaunchVisual.aY     = aY
        LaunchVisual.bX     = aX
        LaunchVisual.bY     = aY
        LaunchVisual.Active = True
    End If
End Sub

Sub Userinput_MouseDragged (EventData As MouseEvent)
    Dim aX, aY, bX, bY As Double
    aX    = (LittleSquare.posX + LittleSquare.sizeX / 2)
    aY    = (LittleSquare.posY + LittleSquare.sizeY / 2)
    bX    = (EventData.X)
    bY    = (EventData.Y)
    If LaunchVisual.Active Then
        LaunchVisual.aX = aX
        LaunchVisual.aY = aY
        LaunchVisual.bX = bX
        LaunchVisual.bY = bY
    End If
End Sub

Sub Userinput_MouseReleased (EventData As MouseEvent)
    Dim angle, dist, aX, aY, bX, bY As Double
    aX    = (LittleSquare.posX + LittleSquare.sizeX / 2)
    aY    = (LittleSquare.posY + LittleSquare.sizeY / 2)
    bX    = (EventData.X)
    bY    = (EventData.Y)
    angle = CalcAngleD(aX, aY, bX, bY)
    dist  = Sqrt(Power((bX-aX),2) + Power((bY-aY),2))
    If LaunchVisual.active Then
        LaunchVisual.bX = bX
        LaunchVisual.bY = bY
        Dim tempVelocity As Vector
        tempVelocity.setAngleAndMagnitudeD(angle, dist * 5)
        LittleSquare.Velocity = tempVelocity
    End If
    LaunchVisual.Active = False
    LaunchVisual.aX     = 0
    LaunchVisual.aY     = 0
    LaunchVisual.bX     = 0
    LaunchVisual.bY     = 0
End Sub

Public Sub CalcAngleD(aX As Double, aY As Double, bX As Double, bY As Double) As Double
    Dim dirX = bX - aX As Double
    Dim dirY = bY - aY As Double
    Return ATan2D(dirY, dirX)
End Sub

Sub DrawShape(input As PhysicalObject2D, color As Paint)
    cvs.DrawRect(input.posX, input.posY, input.sizeX, input.sizeY, color, True, 0)
End Sub

Sub DrawLine(line As Line2D, color As Paint, thickness As Int)
    cvs.DrawLine(line.aX, line.aY, line.bX, line.bY, color, thickness)
End Sub
Vector.bas
B4X:
'Class module
Private Sub Class_Globals
    Private fx As JFX
    Dim Description As String
    Dim Units       As String
    Dim Size        As Double
    Dim AngleD      As Double
    Dim AngleR      As Double
    Dim dirX        As Double
    Dim dirY        As Double
End Sub

'Sets this vector's description and units
Public Sub setDescription(Vector_Description As String, Vector_Units As String)
    Description = Vector_Description
    Units       = Vector_Units
End Sub

'Sets this vector's directional components, magnitude and angle from an imaginary line between two points in space, given their X, Y coordinates.
Public Sub setVectorFromLine(aX As Double, aY As Double, bX As Double, bY As Double)
    dirX      = bX - aX
    dirY      = bY - aY
    AngleD    = ATan2D(dirY, dirX)
    AngleR    = ATan2 (dirY, dirX)
    Size      = Sqrt(Power(dirX, 2) + Power(dirY, 2))
End Sub

'Sets this vector's directional components based on the given size and angle in degrees.
Public Sub setAngleAndMagnitudeD(Degrees As Double, Magnitude As Double)
    AngleD    = Degrees
    AngleR    = Degrees * cPI / 180
    Size      = Magnitude
    dirX      = CosD(Degrees) * Magnitude
    dirY      = SinD(Degrees) * Magnitude
End Sub

'Sets this vector's directional components based on the given size and angle in radians.
Public Sub setAngleAndMagnitudeR(Magnitude As Double, Radians As Double)
    AngleD    = Radians * 180 / cPI
    AngleR    = Radians
    Size      = Magnitude
    dirX      = Cos(Radians) * Magnitude
    dirY      = Sin(Radians) * Magnitude
End Sub

'Updates the vector's directional components, magnitude and angle in radians based on the given angle in degrees.
Public Sub setAngleD(Degrees As Double)
    AngleD    = Degrees
    AngleR    = Degrees * cPI / 180
    dirX      = CosD(Degrees) * Size
    dirY      = SinD(Degrees) * Size
End Sub

'Updates the vector's directional components, magnitude and angle in degrees based on the given angle in radians.
Public Sub setAngleR(Radians As Double)
    AngleD    = Radians * 180 / cPI
    AngleR    = Radians
    dirX      = Cos(Radians) * Size
    dirY      = Sin(Radians) * Size
End Sub
 

Attachments

Last edited:

Jaames

Active Member
Licensed User
Nice , thanks for sharing. One question though, do you manually keep your code that clean? I find your code very easy to read. :)
 

wonder

Expert
Licensed User
Nice , thanks for sharing. One question though, do you manually keep your code that clean? I find your code very easy to read. :)
Thanks for commenting! I'm glad you enjoy the demo! :D
I have to say that I'm extra careful with my public code, but in general yes, I like to keep things tidy and visually appealing. :)
For me, there's nothing worse than going back to an old project and seeing nothing than spaghetti code.
 

wonder

Expert
Licensed User
As requested, here's the B4A version. :)
It's pretty much the same, except for a little change in the collision detection (Ctrl+F: "NEW CODE").

Let me know if it works! :D
 

Attachments

ilan

Expert
Licensed User
As requested, here's the B4A version. :)
It's pretty much the same, except for a little change in the collision detection (Ctrl+F: "NEW CODE").

Let me know if it works! :D
great, wonder. i have tried it and i am getting an error in activity_create

this line is the problem: tempGravity.setAngleAndMagnitudeD(90, 1600)

** Activity (main) Create, isFirst = true **
vector_setangleandmagnituded (B4A line: 57)
tempGravity.setAngleAndMagnitudeD(90, 1600
java.lang.RuntimeException: Object was not initialized.
at anywheresoftware.b4a.debug.Debug.PushSubsStack(Debug.java:122)
at b4a.example.littlesquare.vector._setangleandmagnituded(vector.java:60)
at b4a.example.littlesquare.main._activity_create(main.java:416)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:187)
at b4a.example.littlesquare.main.afterFirstLayout(main.java:102)
at b4a.example.littlesquare.main.access$000(main.java:17)
at b4a.example.littlesquare.main$WaitForLayout.run(main.java:80)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5832)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
 

ilan

Expert
Licensed User
hi wonder,

if i want the square to jump always the same height so there will not be any friction on axis y how can i do that with your example?

when i set CoR to 1 the square jumps higher and higher but i would like to have it jump the same distance every time it hits the ground.

thank you
 

wonder

Expert
Licensed User
have it jump the same distance every time it hits the ground
Then you cannot use the PostCollisionVelocity function, since its formula concerns real-world physics and the behavior you're describing kinda breaks the Newton laws of motion.

That said, for every vertical and downwards collision (where velocity.y > 0 ), you should instruct the object to get just the right amount of vertical upwards velocity. This amount should be a fixed value.

B4X:
If ObjectHitsTheGround And velocity.y > 0 Then
    velocity.y = -300 'For example
End If
You'll have to experiment with different values until you get the desired result.

Remember I'm no physicist, I just like games! :)
 

ilan

Expert
Licensed User
In some tutorials i saw they said that if you want to have that behaivor were the ball does not change his velocity you need to set the friction of this physic object to 0.

An object with no friction should keep his velocity also after hitting a wall/ground.
 

ilan

Expert
Licensed User
I saw this video, it is very interesting.

I dont know how to explain it. (I will try)

When a ball will hit the ground it will never come back to his startpoint because of the friction.

If there where no friction it will bounce back exactly to the place he started to fall because this is the force he came against the second body.

You put a variable in your code CoR that is the elastity of the body but when i put to 1 it will bounce higher then the starting point so something is maybe wrong with the formula?

It should bounce to the same point he started. Or am i wrong?
 

wonder

Expert
Licensed User
When a ball will hit the ground it will never come back to his startpoint because of the friction.
No, no, every time the ball hits the ground, it loses energy. Friction has nothing to do with it.


Source: http://www.gcsescience.com/pen30-energy-ball-bounce.htm

You put a variable in your code CoR that is the elastity of the body but when i put to 1 it will bounce higher then the starting point so something
The in the real-world, the Coefficient of Restitution is something that can only be obtained after the collision happens.



In my LittleSquare Physics demo, we do not have any information regarding the material the object is made of.
All the computer knows is that it weighs 10 kilos, as seen in the code below:
B4X:
PostCollisionVelocity(LittleSquare.Velocity.dirY, 0, 10, EarthMass, 0.6)
Intuitively, we know that a ball made of rubber would bounce really high, but one made of stone would not bounce at all.
Since computer doesn't have any of this information, a good workaround is to introduce the CoR variable à priori.
This way we're able to determine what kind of collision we want, in a very inexpensive way.

Is maybe wrong with the formula?
B4X:
'Given the Conservation of Momentum formula (m1*u1)+(m2*u2) = (m1*v1)+(m2*v2),
'we're able to calculate an object's post collision velocity.
'
'u1: ObjectA pre-collision velocity, u2: ObjectB pre-collision velocity, m1: ObjectA mass in kg, m2: ObjectB mass in kg.
'CoR: Coefficient of Restitution, a value between 0.00(0) and 1.00(0) where 1 represents a perfectly elastic collision.
Sub PostCollisionVelocity(u1 As Double, u2 As Double, m1 As Double, m2 As Double, CoR As Double) As Double
    Dim v1 = ((u1 * (m1 - m2)) + (2 * m2 * u2)) / (m1 + m2) As Double
    Return v1 * CoR
End Sub
It's the conservation of momentum formula, which I think is the correct one for this purpose.
My source: http://physics.tutorvista.com/momentum/conservation-of-momentum.html
 
Last edited:

wonder

Expert
Licensed User
Just in case, here's an alternate version of the formula, taken from the Wikipedia page:
B4X:
Sub PostCollisionVelocity(u1 As Double, u2 As Double, m1 As Double, m2 As Double, CoR As Double) As Double
    Return ((m1 * u1) + (m2 * u2) + (m2 * CoR * (u2 - u1))) / (m1 + m2)
End Sub
It yields the exact same results, I just tested it. These are indeed the correct formulas. :)
 

ilan

Expert
Licensed User
thanx wonder, i will use your example to create my next game. i will need to change a little the code, since i will not swing my character like you do but it is a great example to build from it lots of games. :)
 

BeneBarros

Active Member
Licensed User
Is there any formula for finding the magnitude? Informing degree of tilt and distance from the ball to the goal.
 

wonder

Expert
Licensed User
Last edited:
Top