Using slope to determine angles/modifiers between two points?

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
Khranos
Prole
Posts: 30
Joined: Tue Aug 11, 2015 1:22 am

Using slope to determine angles/modifiers between two points?

Post by Khranos »

Hi all, I'm currently working on a top-down RPG styled game and in addition to WASD movement, I'm attempting to implement "click and hold" movement where the character moves towards your cursor. To do this, I'm attempting to use slope (and the x and y differences that calculate it) to help guide the character toward the cursor. (spoiler -- it isn't working)

The following is the function I'm using to determine slope, where x/y1 are the character's coords and x/y2 are the mouse's coords:

Code: Select all

function slopeFinder(x1, y1, x2, y2)
  local xDif, yDif = (x1 - x2), (y1 - y2) -- ensure that x1/y1 are the player!
  local slope = yDif/xDif
  return slope, yDif, xDif
end
I'm using two libraries currently, being Lume and STI to help with development. Inside of the layer update function of STI, I currently have these equations to help figure out a few more required variables:

Code: Select all

layer.update = function(self, dt)
    local speed = 50
    mouseDist = math.floor(lume.distance(self.player.x, self.player.y, mX, mY)) -- find distance from character and mouse position
    timeM = mouseDist/speed -- finds the time in seconds it'll take at the current speed to reach the required location.
    slope, xDif, yDif = slopeFinder(self.player.x, self.player.y, mX, mY) -- acquire the variables I need from the first function.
    mouseDown = love.mouse.isDown(1)
    if mouseDown then 
    -- mouse stuff here, will post in another code snippet
    end
end
After gathering that information, I attempt to use the following code (when placed in mouseDown from the above snippet) to determine where the player should go, and how far in each direction (and in which direction) based upon the angle and slope:

Code: Select all

      if lume.sign(slope) == 1 then -- positive
        if xDif or yDif ~= 0 or inf then
          self.player.x = self.player.x - (xMod) * dt
          self.player.y = self.player.y - (yMod) * dt
        end
      elseif lume.sign(slope) == -1 then -- negative
        if xDif or yDif ~= 0 or inf then
          self.player.x = self.player.x + (xMod) * dt
          self.player.y = self.player.y + (yMod) * dt
        end
      end
However, the code isn't fully working. It chooses the correct direction (mostly) and initially takes what appears to be the correct speed/angle, yet the character does strange curves and only stops at either xDif == 0 or yDif == 0, never the "origin" of the two.

I've spent many, many hours toiling over this feature and I felt it was time to turn to the community for a nudge in the right direction. Is my current method almost correct/salvageable, or should I take an entirely different approach to this issue?

(attached will be the .love file and a pastebin paste. Much of the ground-work code comes from a tutorial on STI and Tiled implementation from Karai. I have received some help from Bartbes and EntranceJew on the IRC with some of the STI code. Thank you in advance to anybody who attempts to help out in solving this issue!)

Pastebin: http://pastebin.com/1JrrAjz4 (111 lines)

Gif of the issue: (please ignore strange colours/artifacting, and as a side note, are spoilers a thing on this forum?)
issue.gif
issue.gif (370.41 KiB) Viewed 4515 times
Attachments
ProjectSandscapeDev.zip
I would have uploaded a .love, but it gives errors about not having a tilemaps folder (which is there) when being run. It works fine when extracted and run out of an IDE -- another bug I could use help with I suppose.
(514.14 KiB) Downloaded 103 times
Last edited by Khranos on Fri Jun 24, 2016 7:05 pm, edited 1 time in total.
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Using slope to determine angles/modifiers between two points?

Post by s-ol »

You should be using vectors for things like this. I personally usually use HUMP.vec but you can use any library or write your own. Heres what a walk-to usually looks like:

Code: Select all

pos = Vector(12, 17)
target = Vector(29, 30)
PLAYER_SPEED = 250

function update(dt)
  delta = target - pos
  if delta:length() < PLAYER_SPEED*dt then
    target = nil -- we will be done after this frame
  end
  pos += delta:limit(PLAYER_SPEED*dt)
end
Using this approach the player will also have the same absolute speed regardless of direction, instead of walking faster diagonally that on axes.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
marco.lizza
Citizen
Posts: 52
Joined: Wed Dec 23, 2015 4:03 pm

Re: Using slope to determine angles/modifiers between two points?

Post by marco.lizza »

You can simply use "atan2()" to find the angle for the movement, the proceed in that direction...
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Using slope to determine angles/modifiers between two points?

Post by ivan »

Yes, like marco and s-ol mentioned there are easier ways using vectors.

First, you need to find the vector (dx,dy) from the player to the target.

Code: Select all

local tx, ty = love.mouse:getPosition()
local dx = tx - player.x
local dy = ty - player.y
Once you have the vector pointing to the target you can use atan2:

Code: Select all

local angle = math.atan2(dy, dx)
-- note: dy is the first argument
-- atan2 works counter-clockwise where 0 points East
-- so if target == position the player will move East
local nx, ny = math.cos(angle), math.sin(angle)
Or you can normalize the vector and use that:

Code: Select all

local d = math.sqrt(dx*dx + dy*dy)
assert(d > 0, "target == position")
local nx, ny = dx/d, dy/d
Once you have a normalized vector pointing to the target, you can multiply by the desired speed and use it as your velocity:

Code: Select all

local vx = nx*player.speed
local vy = ny*player.speed

player.x = player.x + vx*dt
player.y = player.y + vy*dt
User avatar
pgimeno
Party member
Posts: 3641
Joined: Sun Oct 18, 2015 2:58 pm

Re: Using slope to determine angles/modifiers between two points?

Post by pgimeno »

Khranos wrote:

Code: Select all

        if xDif or yDif ~= 0 or inf then
Not sure what condition you are attempting to state there, but this looks wrong.
User avatar
Khranos
Prole
Posts: 30
Joined: Tue Aug 11, 2015 1:22 am

Re: Using slope to determine angles/modifiers between two points?

Post by Khranos »

I greatly appreciate the responses and help, everybody. It appears I've severely over-complicated a usually simple system.

I'll look into the vector method a bit further, as I've usually found ways around having to use them up until this point -- I now see that has just been a stubborn mistake.

EDIT: @s-ol - I decided to try your method first as I've used hump.timer and hump.camera for other projects before. It appears that ":length" has been changed to ":len" recently, but I'm unsure of what you're doing on the 10th line (as :limit doesn't appear to exist, and the += doesn't appear to be an operator). Due to this, I can't seem to get any variation of your code to work (the values don't move the character/modify something that does move the character from what I can tell).


EDIT: @Ivan - I do like how your method doesn't have to use a library. Your code almost works, but it has a few strange errors (such as the assert not stopping movement at 0). I'd change it myself, but I'm a bit confused on what a few lines do so what I've tried has had no positive effect. (hastebin(s) of what I'm using with comments: http://hastebin.com/momozevewo.lua / http://hastebin.com/izopamejiq.lua , .gif of the issue: https://love2d.org/imgmirrur/C1INNdo.gif ) The issue (which is "tiny text" instead of strikethrough, which I couldn't find) was actually an oversight on my part -- I forgot to add "self." to the player positions for dx and dy. However, I did look up a few tutorials on linear algebra trying to find the issue (which taught me a good bit), so it was for the best. Thank you for a working solution!
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Using slope to determine angles/modifiers between two points?

Post by s-ol »

Khranos wrote:I greatly appreciate the responses and help, everybody. It appears I've severely over-complicated a usually simple system.

I'll look into the vector method a bit further, as I've usually found ways around having to use them up until this point -- I now see that has just been a stubborn mistake.

EDIT: @s-ol - I decided to try your method first as I've used hump.timer and hump.camera for other projects before. It appears that ":length" has been changed to ":len" recently, but I'm unsure of what you're doing on the 10th line (as :limit doesn't appear to exist, and the += doesn't appear to be an operator). Due to this, I can't seem to get any variation of your code to work (the values don't move the character/modify something that does move the character from what I can tell).


EDIT: @Ivan - I do like how your method doesn't have to use a library. Your code almost works, but it has a few strange errors (such as the assert not stopping movement at 0). I'd change it myself, but I'm a bit confused on what a few lines do so what I've tried has had no positive effect. (hastebin(s) of what I'm using with comments: http://hastebin.com/momozevewo.lua / http://hastebin.com/izopamejiq.lua , .gif of the issue: https://love2d.org/imgmirrur/C1INNdo.gif ) The issue (which is "tiny text" instead of strikethrough, which I couldn't find) was actually an oversight on my part -- I forgot to add "self." to the player positions for dx and dy. However, I did look up a few tutorials on linear algebra trying to find the issue (which taught me a good bit), so it was for the best. Thank you for a working solution!
Sorry, I was and am typing from memory on my phone. += Is the same as pos = pos + ... (I write moonscript for my personal projects and theres a += there) and I didn't remember what limit was called, it might be trim(med)? Basically you just shorten the vector length to the distance to the target if it is bigger, bur leave it if it is smaller. That way you don't overshoot the target and boince around but instead hit it directly.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
Khranos
Prole
Posts: 30
Joined: Tue Aug 11, 2015 1:22 am

Re: Using slope to determine angles/modifiers between two points?

Post by Khranos »

s-ol wrote: Sorry, I was and am typing from memory on my phone. += Is the same as pos = pos + ... (I write moonscript for my personal projects and theres a += there) and I didn't remember what limit was called, it might be trim(med)? Basically you just shorten the vector length to the distance to the target if it is bigger, bur leave it if it is smaller. That way you don't overshoot the target and boince around but instead hit it directly.
No worries on the initial mistakes, I appreciate the clarification reply -- the method makes a lot of sense. I'm currently using a different method for the movement, but with the help of your example I'll likely use hump.vector for a few others pieces of position-to-position code.
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Using slope to determine angles/modifiers between two points?

Post by ivan »

Khranos, you don't need to use both atan2 and vector normalization.
One of the two methods should be enough, I will focus on the latter since it's easier to understand:

Code: Select all

-- target point
local tx, ty = love.mouse:getPosition() 

-- vector to target
-- basically the x and y-offset between player and target
local dx = tx - player.x
local dy = ty - player.y

-- distance to target
-- Pythagorean theorem, same as sqrt(dx^2 + dy^2)
local d = math.sqrt(dx*dx + dy*dy)
-- if (dx == 0 and dy == 0) then (d == 0) we will later get division by 0
assert(d > 0, "target == position")

-- normalized/unit vector to target
-- nx, ny points to the target and it's length is 1
-- so think of this vector as the "heading"
local nx, ny = dx/d, dy/d

-- changes the length of the normalized/unit vector nx, ny (while preserving its angle)
local vx = nx*speed
local vy = ny*speed
Skeletonxf
Citizen
Posts: 87
Joined: Tue Dec 30, 2014 6:07 pm

Re: Using slope to determine angles/modifiers between two points?

Post by Skeletonxf »

Might also be helpful to point out why atan2 works
From the reference manual
"Returns the arc tangent of y/x (in radians), but uses the signs of both parameters to find the quadrant of the result. (It also handles correctly the case of x being zero.)"

Image
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 3 guests