In the player class, I have if statements to determine the time to travel based on the resulting distance of the formula. But since it is returning wacky results, the player doesn’t ever move correctly. Short distances take forever (still), and long distances will still go by really quick, and sometimes be really slow.
@WilkeNine_Co - the distance between two points is simple
p1 = vec2(200,300)
p2 = vec2(100,400)
print(p1:dist(p2)) --prints the distance between them
This requires storing your points in vectors (vec2) as above, but that’s usually a good idea, because then you can write
p3=p1+p2 --adds two vectors
p3=p1*5 --multiplies a number by a vector
p1:angleBetween(p2) --angle between vectors
sprite(pic,p1.x,p1.y)
Have a look at the Vector section of the Reference link at top, for more. If you want to do anything useful with Codea, you should definitely learn to use vectors for screen positions.
If you want to move from point A to point B at a given speed, a simple way to do it is like this
--the starting data
speed=2 --pixels to move per frame
p1=vec2(100,200) --start point
p2=vec2(200,500) --end point
--the calculations
d=p1:dist(p2) --distance between points
--now calculate how much to move each frame
--note that m is a vec2, with an x and y value
m=(p2-p1)/d * speed
moving=true --used below
--and every frame, you do this
--the math.min makes sure you don't go past p2
if moving then
p1=p1+math.min(m,p1:dist(p2))
if p1==p2 then moving=false end
end
Make sure you understand the code fully, this is very important to learn. It leads on to “steering behaviour” which you will use later for making objects chase (or run away from) each other. I suggest you try googling “steering behaviour tutorial” to look for a simple explanation and guide on how to do it, because it’s fun.
@Ignatz once again, you’ve managed to make me feel dumb. Which is a good thing. I was looking through Vectors, but I totally missed “:dist()”. I use vectors for positions most of the time, in fact that’s what I use for my randomly generated chests for their positions. But since I was using Tweens for movement, I had to use tables. Thanks so much!
I feel dumb every day, it’s part of being a programmer.
And at the risk of making you feel dumb one last time, a vec2 is a table, so it works with a tween.
@Ignatz however, I did copy your movement solution into my touched(touch)
function, and replaced my Player class’ self.position
variable to a vec2()
. And the Sprite’s positions are using that variables x and y values, but now it says there is a problem because of comparing userdata and something else.
…you want code right? Here it is:
Player Class:
Player = class()
function Player:init()
--self.position = {x = 384, y = 512}
self.position = vec2(WIDTH/2,HEIGHT/2)
self.health = 100
self.damage = 5
self.gold = 200
end
function Player:draw()
sprite("Documents:Character",self.position.x,self.position.y,40,40)
end
function Player:touched(touch)
-- This is where we will manage the movement of the character, and also how the player will attack (if tapped an enemy).
s=2 --pixels to move per frame
p1=self.position--start point
p2=vec2(touch.x,touch.y) --end point
--the calculations
d=p1:dist(p2) --distance between points
--now calculate how much to move each frame
--note that m is a vec2, with an x and y value
m=(p2-p1)/d * s
moving=true --used below
--and every frame, you do this
--the math.min makes sure you don't go past p2
if moving then
p1=p1+math.min(m,p1:dist(p2))
if p1==p2 then moving=false end
end
--tween(timing,self.position,{x = touch.x, y = touch.y})
end
function Player:overlaps()
if self.left > Sentinel().right or self.right < Sentinel().left or
self.bottom > Sentinel().top or self.top < Sentinel().bottom then
print("Touched a chest.")
else
return true
end
end
@WilkieNine_Co - See numbered comments in the code and explanation below. You needed to adapt what I wrote, also I improved it.
function Player:init()
--self.position = {x = 384, y = 512}
self.position = vec2(WIDTH/2,HEIGHT/2)
self.health = 100
self.damage = 5
self.gold = 200
self.speed=2 -- speed. [1]
end
function Player:draw()
if self.endPosition then. --[2]
self.position=self.position+
self.direction*math.min(self.speed,self.position:dist(self.endPosition)) --[3]
if self.position==self.endPosition then self.endPosition=nil end --[4]
end
sprite("Documents:Character",self.position.x,self.position.y,40,40)
end
function Player:touched(touch)
-- This is where we will manage the movement of the character, and also how the player will attack (if tapped an enemy).
self.endPosition=vec2(touch.x,touch.y) --end point. --[5]
--now calculate how much to move each frame
local d=self.position:dist(self.endPosition) --distance between points
self.direction=(self.endPosition-self.position)/d --[6]
end
[1] define player speed in init
[2] first see point [5] below, then - as long as self.endPosition exists, we know we are moving and need to adjust our position.
[3] read point [6] first, then - we move by the lesser of our speed and the remaining distance, multiplied by the direction we stored.
[4] delete the end position when we get there
[5] set the end position and store it as a class variable so it will be remembered
[6] calculate direction we need to move in. We get this by subtracting current position from touch position. But the answer has a length, too. We want to go at a constant speed, so what we’ll do is divide our answer by its length, giving it a length of 1. Then if we want to move S pixels, we just multiply it by S. If this isnt’ clear, see my post here.
@Ignatz I see where this is going, but I am looking at everything, and I can’t seem to understand why there is an error saying.
“attempt to index a nil value (Global ‘Player’)”
I don’t see any square brackets in the code suggesting indexing. And there isn’t anything that goes “Player().”
it runs fine for me, so have a look at the rest of your code. Which line is breaking?
btw, there was an even easier solution, to use the tween and set the time as (d / self.speed) / 60
, but you wouldn’t have learned as much about vectors that way.
“attempt to index a nil value (Global ‘Player’)”
You are not using classes correctly. In setup, do not explicitly call Player:init()
. Instead, create an instance of the Player class, and assign it to a variable, eg player1 = Player()
. You do not need :init()
, init is run automatically when you create a new class instance. Then, in draw and in touched, you call the class’s methods by referring to the instance of the class you have created, so player1:draw()
, player1:touched(touch)
etc
Thanks for all your help, guys! I have figured out my own way of doing this. I got it all working now, and nothing is wrong with my code anymore. I made my own fix. I just used Ignatz’s mentioning of the vec2():dist()
function, and combined that with my if statement approach.