Why “without physic engine”?
Firstly, I have to say that physic engine is an excellent tool for simulating physical interactions, a great framework for making physic based games. Thousands (or hundreds of thousands maybe) of physic based were made by this awesome engine. People are satisfied with it.
Me too, until I tried to make a simple game likes an archer using a bow to shoot an apple. I’ve found some interesting tutorials on the Corona’s community to make it. Yesss, it’s easy! 5 minutes making with couples of code to load the arrow’s image, turn it into physic body and then apply the force. That’s it! But wait, there’s something strange with the arrow’s motion. It looked like your throw the arrow than you shoot it: the arrow is sometime hit on the ground by its tail especially when you apply a weak force on it. What I expected to see is the arrow’s tip should always be heading to its direction. Please see the followed figures:
Figure 1 – Physic engine simulation |
Figure 2 – What to be expected |
Although I tried to set the mass of the arrow’s tip very heavy to make the tip always head down, the issue was not perfectly solved. Maybe I didn’t apply the physic engine correctly but I couldn’t find any other helps in using physic engine. So I did it in my way.
Here’re things to do:
– How to simulate throwing an object upward diagonally
– Keep the arrow’s tip heading to its direction
– Collision checking!!!***
First to do is to simulate the motion of an object thrown upward, please refer to Wiki (projectile motion) for more maths and physics details.
The algorithm to update the arrow’s position after it is released from the bow is as following pseudo code:
Definitions
– arrow: is the arrow; arrow.x and arrow.y are x and y coordinates of the arrow respectively.
– v0 is the starting velocity.
– angle: angle is the start angle.
– g: gravity
– vx, vy are the horizontal and vertical velocities of the arrow.
Algorithm
– Start timer, t = 0
– For each interval
- Set t = t + 1
- Update arrow’s position by
- Calculate vx and vy by given formulas:
- vx = v0 * cos(alpha)
- vy = v0 * sin(alpha) – g * t; if the arrow reaches its max height then switch the direction of the gravity g = g * (-1)
- Set arrow.x = arrow.x + vx
- Set arrow.y = arrow.y + vy
- — set arrow’s rotation here!!!
- Calculate vx and vy by given formulas:
- Check collision
- If ‘yes’, there is collision, invoke the callback function.
– Until the arrow’s position is out of bound or there is another instruction happen.
Then we adjust the angle of the arrow so as the arrow’s head is always head to motion direction. According to Wiki (projectile motion) explanation, the velocity of the arrow at a time is the sum of the two velocity vectors vx and vy which is described on the following figure:
So, the angle of the arrow can be calculated as: arctan(vy/vx). Then we apply to the arrow by:
arrow.rotation = math.atan2(vy, vx) * 180 / math.pi
And here is the code for updating arrow’s position in LUA, Corona SDK:
local angle = self.bowui.rotation * math.pi / 180
local g = -0.98 -- gravity
— horizontal velocity
local vx = self.startSpeed * math.cos(angle)
— update new position
self.arrowui.x = self.arrowui.x + vx
local h = self.arrowui.y – self.bowui.y
— check if h is max height then switch the gravity direction
if (h == self.startSpeed ^ 2 * math.sin(angle) ^ 2 / (2 * g)) then
g = g * (-1)
end
— vertical velocity
local vy = self.startSpeed * math.sin(angle) – g * tCounter
— update new position
self.arrowui.y = self.arrowui.y + vy
— compute the angle of the arrow
local arAngle = math.atan2(vy, vx)
self.arrowui.rotation = arAngle * 180 / math.pi
— timer counter
tCounter = tCounter + 1
— invoke the collision detection function
local collided = self.isCollided()
— raise collision event
if collided ~= nil then
self.onCollision(collided)
end
Second part: collision check at here.
Please see the whole source code. Enjoy!