how to move character physics locally amazon lumberyard - lua

I'm having some issues with moving a character physics capsule in local space. No matter what rotation I give it, it still moves in relation to world space. I've attached a mesh component, a character physics component, an input component with my input, and a lua script to it. Here's the movement code in my lua script:
function PlayerController:HandleInput(floatValue)
local currentBusId = InputEventNotificationBus.GetCurrentBusId()
local forwardSpeed = 0.0
local sideSpeed = 0.0
local rotate = 0.0
local fixedSpeed = self.Properties.Speed * 0.01
local fixedStrafeSpeed = self.Properties.StrafeSpeed * 0.01
if(currentBusId == self.forwardBusId) then
forwardSpeed = floatValue
end
if(currentBusId == self.leftBusId) then
sideSpeed = -floatValue
end
if(currentBusId == self.rotateBusId) then
rotate = floatValue
end
PhysicsComponentRequestBus.Event.AddImpulse(self.entityId, Vector3(fixedStrafeSpeed * sideSpeed, fixedSpeed * forwardSpeed, 0.0))
end
I was wondering what the best way to move it in local space would be.

If you wish to move a character in local space then you will need to compute it based on the orientation of your character. In C++, it would be like:
AZ::Transform myLocation;
TransformBus::EventResult(myLocation, GetEntityId(), &TransformBus::Events::GetWorldTM);
const auto q = Quaternion::CreateFromTransform(myLocation);
const Vector3 disp = q * AZ::Vector3::CreateAxisY( 1.f );
myLocation.SetTranslation(myLocation.GetTranslation() + disp);

Related

Moving an object with a rotation

I'm trying to make a freecam sort of tool for a rather obscure game. I'm trying to figure out how to move the XY position of the freecam based on the car's rotation value, but I'm struggling to do so. I've tried using Calculating X Y movement based on rotation angle? and modifying it a bit, but it doesn't work as intended. The game's rotation uses a float that ranges from -1 to 1, -1 being 0 degrees and 1 being 360 degrees.
Putting rot at -1 corresponds to X+
Putting rot at 0 corresponds to Z+
Putting rot at 1 corresponds to X-
Here's my cheat engine code:
speed = 10000
local yaw = math.rad((180*(getAddressList().getMemoryRecordByDescription('rot').Value))+180)
local px = getAddressList().getMemoryRecordByDescription('playerx').Value
local py = getAddressList().getMemoryRecordByDescription('playery').Value
local pz = getAddressList().getMemoryRecordByDescription('playerz').Value
local siny = math.sin(yaw) -- Sine of Horizontal (Yaw)
local cosy = math.cos(yaw) -- Cosine of Horizontal (Yaw)
getAddressList().getMemoryRecordByDescription('playerx').Value = ((getAddressList().getMemoryRecordByDescription('playerx').Value)+(cosy*speed))
getAddressList().getMemoryRecordByDescription('playerz').Value = ((getAddressList().getMemoryRecordByDescription('playerz').Value)+(siny*speed))
print(yaw)

2D Hexagon grid

I recently made a simple lua class (this is using the love2D engine) that made a hexagon by plotting 6 vertices and tracing them with lines, one of the things I wanted to be able to expand to was a hexagon grid but I didn't know how
All of the websites I found didn't help and I couldn't understand why.
Here's the function where I plot the vertices based on it's radius and x and y position.
My hexagons are in a pointy-topped style.
function hexagon.new(x,y,radius)
local hexagon=setmetatable({},hexagon)
hexagon.Vertices={}
hexagon.x=x or 0
hexagon.radius = radius or 10
hexagon.y=y or 0
for i=0,6 do
local angle = 2 * math.pi / 6 * (i + .5) -- 1 is what is multipled to 90(2*math.pi) so 1*90=90(flat-topped), 0.5*90=45(pointy-topped)
local x = hexagon.x + hexagon.radius * math.cos(angle)
local y = hexagon.y + hexagon.radius * math.sin(angle)
hexagon.Vertices[i]= {x=x,y=y}
end
return hexagon
end
Hope i understood well.
Here is working example of how to create hex and then draw it to grid with as simplified functions as possible, using Love2d engine (v. 0.10.2).
important: made for hex which has all six points on circle, using radius.
this approach is not suitable for all hex shapes.
------------------------------
--lets create hex data with center at position 0,0
--using only simple functions
--table of 6 points
hex = {};
--radius for hex which has all 6 points lying on circle
hex.radius = 10;
--simple loop to generate hex
local i = 1
repeat
--full circle has 2 radians and hex has 6 points
--so 1/3 pi increase per point
local direction = math.pi/3 * (i+0.5)
--rotate hex by 90 degrees: direction = math.pi/3 * i
--generate empty table to insert point coordinations to
hex[i] = {};
--set x,y coordinates
hex[i].x = hex.radius*math.cos( direction )
hex[i].y = hex.radius*math.sin( direction )
i = i+1
until i > 6
------------------------------
------------------------------
function love.draw( dt )
--get distance between 2 hex tiles using trigonometry
local jxOffset = hex.radius*-math.tan( math.pi/1.5 ) --or math.sqrt(3) * hex.radius
--offset new lines by this value
local ixOffset = jxOffset/4
--use direction and distance to get "y" offset between lines
--we got distance in jxOffset, so we just apply direction
local iyOffset = jxOffset*math.sin( math.pi/3 )
--"i" = line, "j" = tile in line
local i = 1
repeat
local j = 1
repeat
love.graphics.push()
--offset drawable position (or draw hex tile at position):
love.graphics.translate( ixOffset+j*jxOffset, i*iyOffset )
--draw poly-line between all hex points - creating hex
love.graphics.line( hex[1].x,hex[1].y, hex[2].x,hex[2].y, hex[3].x,hex[3].y, hex[4].x,hex[4].y, hex[5].x,hex[5].y, hex[6].x,hex[6].y, hex[1].x,hex[1].y );
love.graphics.pop()
j = j+1
until j > 5
--invert for each new line
--keep lines from increasing x coordinates
ixOffset = -ixOffset
i = i+1
until i > 5
end
------------------------------
Excuse me if i made mistake somewhere, ran the code and worked properly on my side. If you discover anything off, comment and i will try to fix it.

Corona source of gravity is body

I want to make a moon rotating around a planet, or something along that analogy. So is there something I can call for physics.setGravity(0,0) that changes the position that the gravity pulls towards, particularly assigning that to be a physics body? If not, simply a specific x-y coordinate will be fine.
local moon = display.newImage ("moon.png")
physics.addBody(moon, {bounce=0, density=1.0})
local earth = display.newImage ("earth.png")
physics.addBody(earth, {bounce=0, density=1.0})
Thanks
Instead of playing around with the setGravity option, I think you should just rotate the images using something like this:
local physics = require("physics")
physics.start()
physics.setGravity(0, 0)
display.setStatusBar( display.HiddenStatusBar )
local background = display.newImage( "space.png", true )
background.x = display.contentWidth / 2
background.y = display.contentHeight / 2
local earth = display.newImage( "earth.png" )
earth.x = 100; earth.y = 200
earth.xScale = 0.7
earth.yScale = 0.7
physics.addBody( earth, {radius=40 } )
earth.bodyType = "static"
local moon = display.newImage( "moon.png", 40, 40)
moon.x = earth.x + 80
moon.y = earth.y
moon.xScale = 0.3
moon.yScale = 0.3
moon.rotation = 100
physics.addBody( moon, {radius=10 } )
moon.bodyType = "dynamic"
myJoint = physics.newJoint( "pivot", moon, earth, earth.x, earth.y )
local orbit = function( event )
moon.rotation = moon.rotation + 1.5
end
Notice that I am not really mucking about with setGravity, but the speed of the rotation is defined in the orbit function.
The above code assumes you just have image files to represent your planetary bodies. If so, you will have to play around with the constants above (earth.x and earth.y, scaling values, etc) to make the whole system LOOK right for the images you choose.
Good luck!
No - you can change gravity but it will pull/push only in the direction you have it set to, never to any point. You'd likely use a sensor (invisible) around your earth and apply force to draw things towards it when the collision event between the moon/other and sensor began.

How do I draw a segment of a circle in Lua in Corona?

long time ago is last time I needed that :)
I simple like to create a circle with a segment and a different filling or transparent.
So i just have like a stopwatch filling up the circle by time (60 seconds).
function kind a > showsegment (xcircle,ycircle,radius, seconds) :}#
any short lines leading to that solution, are welcome.
The code needs to work within the Corona Framework, in Lua.
I don't think you can. Using image with alpha and tint is not an option?
Yes, you'll have to create 60 objects, one for every tick, but images are cached anyway, so you only loading it and allocating memory for it once. Every next instance is cheap.
I am not sure if this is what you're looking for, but seeing the question it made me curious, so I messed around with it and figured this (if it's what you're looking for):
tick = 0;
ticks = {};
cr = 250; -- Circle radius
hr = 0.9; -- hand radius
hw = 10; -- hand width
mr = 0.25; -- middle radius (fg)
bg = display.newCircle(cr, cr, cr); -- background
for i=1,360 do
local w = hr * (cr * 2);
local x = (w/2)+(((cr*2) - w)/2);
local t = display.newRect(x,x,hw,w);
t:rotate(i-1);
t:setFillColor(0,0, 0);
table.insert(ticks, t);
end
function drawTick(e)
print("tick "..tick);
local dg = display.newGroup();
local w = hr * (cr * 2);
local x = (w/2)+(((cr*2) - w)/2);
local t = display.newRect(dg, 0, -w/4, 10, w/2);
dg.x = x;
dg.y = x;
t:setFillColor(0, 1, 0);
dg:rotate(tick-1);
table.insert(ticks, t);
fg = display.newCircle(cr,cr,mr*cr);
if tick < 361 then
tick = tick + 1
timer.performWithDelay(50, drawTick);
end
end
timer.performWithDelay(0, drawTick);
EDIT: I cleaned up the code a bit.

physics game programming box2d - orientating a turret-like object using torques

This is a problem I hit when trying to implement a game using the LÖVE engine, which covers box2d with Lua scripting.
The objective is simple: A turret-like object (seen from the top, on a 2D environment) needs to orientate itself so it points to a target.
The turret is on the x,y coordinates, and the target is on tx, ty. We can consider that x,y are fixed, but tx, ty tend to vary from one instant to the other (i.e. they would be the mouse cursor).
The turret has a rotor that can apply a rotational force (torque) on any given moment, clockwise or counter-clockwise. The magnitude of that force has an upper limit called maxTorque.
The turret also has certain rotational inertia, which acts for angular movement the same way mass acts for linear movement. There's no friction of any kind, so the turret will keep spinning if it has an angular velocity.
The turret has a small AI function that re-evaluates its orientation to verify that it points to the right direction, and activates the rotator. This happens every dt (~60 times per second). It looks like this right now:
function Turret:update(dt)
local x,y = self:getPositon()
local tx,ty = self:getTarget()
local maxTorque = self:getMaxTorque() -- max force of the turret rotor
local inertia = self:getInertia() -- the rotational inertia
local w = self:getAngularVelocity() -- current angular velocity of the turret
local angle = self:getAngle() -- the angle the turret is facing currently
-- the angle of the like that links the turret center with the target
local targetAngle = math.atan2(oy-y,ox-x)
local differenceAngle = _normalizeAngle(targetAngle - angle)
if(differenceAngle <= math.pi) then -- counter-clockwise is the shortest path
self:applyTorque(maxTorque)
else -- clockwise is the shortest path
self:applyTorque(-maxTorque)
end
end
... it fails. Let me explain with two illustrative situations:
The turret "oscillates" around the targetAngle.
If the target is "right behind the turret, just a little clock-wise", the turret will start applying clockwise torques, and keep applying them until the instant in which it surpasses the target angle. At that moment it will start applying torques on the opposite direction. But it will have gained a significant angular velocity, so it will keep going clockwise for some time... until the target will be "just behind, but a bit counter-clockwise". And it will start again. So the turret will oscillate or even go in round circles.
I think that my turret should start applying torques in the "opposite direction of the shortest path" before it reaches the target angle (like a car braking before stopping).
Intuitively, I think the turret should "start applying torques on the opposite direction of the shortest path when it is about half-way to the target objective". My intuition tells me that it has something to do with the angular velocity. And then there's the fact that the target is mobile - I don't know if I should take that into account somehow or just ignore it.
How do I calculate when the turret must "start braking"?
Think backwards. The turret must "start braking" when it has just enough room to decelerate from its current angular velocity to a dead stop, which is the same as the room it would need to accelerate from a dead stop to its current angular velocity, which is
|differenceAngle| = w^2*Inertia/2*MaxTorque.
You may also have some trouble with small oscillations around the target if your step time is too large; that'll require a little more finesse, you'll have to brake a little sooner, and more gently. Don't worry about that until you see it.
That should be good enough for now, but there's another catch that may trip you up later: deciding which way to go. Sometimes going the long way around is quicker, if you're going that way already. In that case you have to decide which way takes less time, which is not difficult, but again, cross that bridge when you come to it.
EDIT:
My equation was wrong, it should be Inertia/2*maxTorque, not 2*maxTorque/Inertia (that's what I get for trying to do algebra at the keyboard). I've fixed it.
Try this:
local torque = maxTorque;
if(differenceAngle > math.pi) then -- clockwise is the shortest path
torque = -torque;
end
if(differenceAngle < w*w*Inertia/(2*MaxTorque)) then -- brake
torque = -torque;
end
self:applyTorque(torque)
This seems like a problem that can be solved with a PID controller. I use them in my work to control a heater output to set a temperature.
For the 'P' component, you apply a torque that is proportional to the difference between the turret angle and the target angle i.e.
P = P0 * differenceAngle
If this still oscillates too much (it will a bit) then add an 'I' component,
integAngle = integAngle + differenceAngle * dt
I = I0 * integAngle
If this overshoots too much then add a 'D' term
derivAngle = (prevDifferenceAngle - differenceAngle) / dt
prevDifferenceAngle = differenceAngle
D = D0 * derivAngle
P0, I0 and D0 are constants that you can tune to get the behaviour that you want (i.e. how fast the turrets respond etc.)
Just as a tip, normally P0 > I0 > D0
Use these terms to determine how much torque is applied i.e.
magnitudeAngMomentum = P + I + D
EDIT:
Here is an application written using Processing that uses PID. It actually works fine without I or D. See it working here
// Demonstration of the use of PID algorithm to
// simulate a turret finding a target. The mouse pointer is the target
float dt = 1e-2;
float turretAngle = 0.0;
float turretMass = 1;
// Tune these to get different turret behaviour
float P0 = 5.0;
float I0 = 0.0;
float D0 = 0.0;
float maxAngMomentum = 1.0;
void setup() {
size(500, 500);
frameRate(1/dt);
}
void draw() {
background(0);
translate(width/2, height/2);
float angVel, angMomentum, P, I, D, diffAngle, derivDiffAngle;
float prevDiffAngle = 0.0;
float integDiffAngle = 0.0;
// Find the target
float targetX = mouseX;
float targetY = mouseY;
float targetAngle = atan2(targetY - 250, targetX - 250);
diffAngle = targetAngle - turretAngle;
integDiffAngle = integDiffAngle + diffAngle * dt;
derivDiffAngle = (prevDiffAngle - diffAngle) / dt;
P = P0 * diffAngle;
I = I0 * integDiffAngle;
D = D0 * derivDiffAngle;
angMomentum = P + I + D;
// This is the 'maxTorque' equivelant
angMomentum = constrain(angMomentum, -maxAngMomentum, maxAngMomentum);
// Ang. Momentum = mass * ang. velocity
// ang. velocity = ang. momentum / mass
angVel = angMomentum / turretMass;
turretAngle = turretAngle + angVel * dt;
// Draw the 'turret'
rotate(turretAngle);
triangle(-20, 10, -20, -10, 20, 0);
prevDiffAngle = diffAngle;
}
Ok I believe I got the solution.
This is based on Beta's idea, but with some necessary tweaks. Here it goes:
local twoPi = 2.0 * math.pi -- small optimisation
-- returns -1, 1 or 0 depending on whether x>0, x<0 or x=0
function _sign(x)
return x>0 and 1 or x<0 and -1 or 0
end
-- transforms any angle so it is on the 0-2Pi range
local _normalizeAngle = function(angle)
angle = angle % twoPi
return (angle < 0 and (angle + twoPi) or angle)
end
function Turret:update(dt)
local tx, ty = self:getTargetPosition()
local x, y = self:getPosition()
local angle = self:getAngle()
local maxTorque = self:getMaxTorque()
local inertia = self:getInertia()
local w = self:getAngularVelocity()
local targetAngle = math.atan2(ty-y,tx-x)
-- distance I have to cover
local differenceAngle = _normalizeAngle(targetAngle - angle)
-- distance it will take me to stop
local brakingAngle = _normalizeAngle(_sign(w)*2.0*w*w*inertia/maxTorque)
local torque = maxTorque
-- two of these 3 conditions must be true
local a,b,c = differenceAngle > math.pi, brakingAngle > differenceAngle, w > 0
if( (a and b) or (a and c) or (b and c) ) then
torque = -torque
end
self:applyTorque(torque)
end
The concept behind this is simple: I need to calculate how much "space" (angle) the turret needs in order to stop completely. That depends on how fast the turret moves and how much torque can it apply to itself. In a nutshell, that's what I calculate with brakingAngle.
My formula for calculating this angle is slightly different from Beta's. A friend of mine helped me out with the physics, and well, they seem to be working. Adding the sign of w was my idea.
I had to implement a "normalizing" function, which puts any angle back to the 0-2Pi zone.
Initially this was an entangled if-else-if-else. Since the conditions where very repetitive, I used some boolean logic in order to simplify the algorithm. The downside is that, even if it works ok and it is not complicated, it doesn't transpire why it works.
Once the code is a little bit more depurated I'll post a link to a demo here.
Thanks a lot.
EDIT: Working LÖVE sample is now available here. The important stuff is inside actors/AI.lua (the .love file can be opened with a zip uncompressor)
You could find an equation for angular velocity vs angular distance for the rotor when accelerating torque is applied, and find the same equation for when the braking torque is applied.
Then modify the breaking equation such that it intesects the angular distance axis at the required angle. With these two equations you can calculate the angular distance at which they intersect which would give you the breaking point.
Could be totally wrong though, not done any like this for a long time. Probably a simpler solution. I'm assuming that acceleration is not linear.
A simplified version of this problem is pretty simple to solve. Assume the motor has infinite torque, ie it can change velocity instantaneously. This is obviously not physically accurate but makes the problem much simpler to solve and in the end isn't a problem.
Focus on a target angular velocity not a target angle.
current_angle = "the turrets current angle";
target_angle = "the angle the turret should be pointing";
dt = "the timestep used for Box2D, usually 1/60";
max_omega = "the maximum speed a turret can rotate";
theta_delta = target_angle - current_angle;
normalized_delta = normalize theta_delta between -pi and pi;
delta_omega = normalized_deta / dt;
normalized_delta_omega = min( delta_omega, max_omega );
turret.SetAngularVelocity( normalized_delta_omega );
The reason this works is the turret automatically tries to move slower as it reaches its target angle.
The infinite torque is masked by the fact that the turret doesn't try to close the distance instantaneously. Instead it tries to close the distance in one timestep. Also since the range of -pi to pi is pretty small the possibly insane accelerations never show themselves. The maximum angular velocity keep the turret's rotations looking realistic.
I've never worked out the real equation for solving with torque instead of angular velocity, but I imagine it will look a lot like the PID equations.

Resources