Movement Relative to Mouse

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Movement Relative to Mouse

Post by Ortimh »

I try to make a top-down shooter with movement relative to mouse. I can make the player followed the mouse, but only forward and backward. While strafe left and right, I was getting dizzy. For now I use movement relative to keyboard but if you can help me, I will greatly appreciate it. And of course the code:

Code: Select all

		local speed = 0
		local running = love.keyboard.isDown("lshift") or love.keyboard.isDown("rshift")
		
		if (love.keyboard.isDown("w") or love.keyboard.isDown("a") or love.keyboard.isDown("s") or love.keyboard.isDown("d")) then
			speed = private.speed.walk
			
			if (running) then
				speed = private.speed.run
			end
			
			if (private.sprint.state) then
				speed = private.speed.sprint
			end
		end
		
		if (love.keyboard.isDown("s")) then
			speed = -speed
		end
		
		local mouseX, mouseY = private.camera:getWorldPosition(love.mouse.getPosition())
		local angle = game.math.angle(mouseX, mouseY, private.x, private.y)
		private.x, private.y = private.x + math.cos(angle) * (speed * dt), private.y + math.sin(angle) * (speed * dt)
		private.rotation = angle + math.rad(90)
The code is in update function and uses movement relative to mouse not to keyboard. game.math.angle function only returns math.tan2(y1 - y2, x1 - x2). I didn't know why I added math.rad(90) for rotation but it worked.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Movement Relative to Mouse

Post by ivan »

Hello, the following line:

Code: Select all

private.x, private.y = private.x + math.cos(angle) * (speed * dt), private.y + math.sin(angle) * (speed * dt)
Means: move "private" from his current position towards the "position of the mouse" at the speed of "speed*dt".
Note that movement is confined along the axis alone: private.x,private.y -> mousex,mousey
Could you explain what type of movement you expect since the A&D keys don't affect the movement in your code example.
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Re: Movement Relative to Mouse

Post by Ortimh »

My code is not an example. It is the code that I was using. And yeah, only W & S keys will respond. I want A & D make player go left and right based on the mouse position. Note private is the player. I'm using public and private kind of things.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Movement Relative to Mouse

Post by Robin »

When strafing, you'll need to swap math.cos and math.sin, because movement will be rotated by 90 degrees. I think. I didn't test it. But it was something like that.
Help us help you: attach a .love.
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Movement Relative to Mouse

Post by micha »

Robin wrote:When strafing, you'll need to swap math.cos and math.sin, because movement will be rotated by 90 degrees. I think. I didn't test it. But it was something like that.
Almost. To rotate a vector (x,y) by 90 degrees, swap the two components and multiply one by -1. You get (y,-x) and (-y,x).
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Re: Movement Relative to Mouse

Post by Ortimh »

micha wrote:Almost. To rotate a vector (x,y) by 90 degrees, swap the two components and multiply one by -1. You get (y,-x) and (-y,x).
What components should I swap? And how to implement your solution in my code? Because I don't understand your point here. Can you provide a code or more explanation?
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Movement Relative to Mouse

Post by micha »

Ortimh wrote:Can you provide a code or more explanation?
Sure. Here is some code (I renamed "private" to "p" so that it is shorter):

Code: Select all

local mouseX, mouseY = p.camera:getWorldPosition(love.mouse.getPosition())
local angle = game.math.angle(mouseX, mouseY, p.x, p.y)
local dx = math.cos(angle) -- this calculates the direction the player is looking at
local dy = math.sin(angle)

if love.keyboard.isDown("w") then -- this is what you have already
  p.x = p.x + dx * speed * dt
  p.y = p.y + dy * speed * dt
end
if love.keyboard.isDown("s") then
  p.x = p.x - dx * speed * dt
  p.y = p.y - dy * speed * dt
end
-- now comes the part with the swapping:
if love.keyboard.isDown("a") then
  p.x = p.x - dy * speed * dt
  p.y = p.y + dx * speed * dt
end
if love.keyboard.isDown("d") then
  p.x = p.x + dy * speed * dt
  p.y = p.y - dx * speed * dt
end
Note that in the bottom two if-statements, the dx and the dy are swapped and one of them has a minus (-). That is what I meant initially. I hope this becomes clear from the code.
To make this work in your original code you need to remove the lines of code, where you change the angle of the player and where you change the "speed" to "-speed".
This code is untested, I might have switched some +/- in the last two statements.
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Re: Movement Relative to Mouse

Post by Ortimh »

You switched the swapping part, key A to D and D to A. Thanks, micha. Your code works perfectly fine for me.
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: Movement Relative to Mouse

Post by Positive07 »

Made it shorter, added the possibility to change the controls, enjoy!

Code: Select all

local mouseX, mouseY = p.camera:getWorldPosition(love.mouse.getPosition())
local angle = game.math.angle(mouseX, mouseY, p.x, p.y)
local dx = math.cos(angle) -- this calculates the direction the player is looking at
local dy = math.sin(angle)

local keys = {"w","s","a","d"} --Advantage here, you can now change the keys in your options menu for example (just remember to maintain the order)

local movement = {
	{x = +1, y = +1}, --Up
	{x = -1, y = -1}, --Down
	{x = -1, y = +1}, --Left
	{x = +1, y = -1}, --Right
}

for i=1, 4 do 
	if love.keyboard.isDown(key[i]) then
		p.x = p.x + movement[i].x * dx * speed * dt
		p.y = p.y + movement[i].y * dy * speed * dt
	end
end
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Movement Relative to Mouse

Post by ivan »

Good examples by Micha and Positive,
I would like to point out that, if you split the movement
vector in tangent and heading components
it becomes a little bit simpler.
Just start with the movement vector relative to the player's heading.
Then you transform that vector.
Here is an untested example

Code: Select all

-- unit heading vector, alternative to atan2
p.hx, p.hy = 1, 0 -- initial player heading

function p.getHeading()
  return p.hx, p.hy
end

function p.setHeading(tx, ty) -- given a 'target' point
  local dx, dy = tx - p.x, ty - p.y -- vector to target
  -- don't update heading when the player is "on top" of the target
  -- atan2 would return 0 radians in this case, which usually means "east" or "right"
  local d = dx*dx + dy*dy -- distance to target^2
  if d > 0 then
    d = math.sqrt(d)
    p.hx, p.hy = dx/d, dy/d
  end
end

-- returns the 'desired' movement in local space
function p.getRelativeMovement()
  -- first, find the relative movement in local space
  local ax, ay = 0, 0
  if love.keyboard.isDown("w") then
    ay = -1
  elseif love.keyboard.isDown("s") then
    ay = 1
  end
  if love.keyboard.isDown("a") then
    ax = -1
  elseif love.keyboard.isDown("d") then
    ax = 1
  end
  return ax, ay
end

function p.processMovement(dt)
  -- update the heading
  local mx, my = love.mouse.getPosition() -- mouse position
  local tx, ty = p.camera:getWorldPosition(mx, my) -- target position
  p.setHeading(tx, ty)
  
  -- find the heading
  local hx, hy = p.getHeading()

  -- find the relative movement vector
  local ax, ay = p.getRelativeMovement()

  -- find the world-space movement
  local ahx, ahy = hx*ax, hy*ay -- along the 'heading' axis
  local atx, aty = hx*-ay, hy*ax -- along the 'tangent' axis
  local avx, avy = ahx + atx, ahy + aty -- combined
  -- optional: normalize so we don't move faster diagonally
  local a = avx*avx + avy*avy
  if a > 0 then
    a = math.sqrt(a)
    avx, avy = avx/a, avy/a
  end

  -- update position
  local step = p.speed*dt -- edit:renamed
  p.x = p.x + avx*step
  p.y = p.y + avy*step
end
Last edited by ivan on Sat May 30, 2015 8:15 am, edited 2 times in total.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot], pgimeno and 58 guests