Touch and Go

I am starting a new thread to address a specific question. I searched the forums and didn’t find anything. If I missed it, let me know, and I will follow that lead. But, here is my situation:
I am working on a small project, and would like to have a sprite move in the direction of where I am touching on the screen. Below is my code (that does not work). I made comments what I think the code should be doing.

-- #Main

--displayMode(FULLSCREEN)
supportedOrientations(CurrentOrientation)

-- Use this function to perform your initial setup
function setup()
    m = Mine()
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(142, 88, 38, 255)
    mineTouch()
    -- Do your drawing here
    m:draw()
   
    
end

function mineTouch()
    if CurrentTouch.state == BEGAN and MOVING then
       if CurrentTouch.x < WIDTH then -- if touching inside the screen
        if CurrentTouch.y < HEIGHT then
            -- move the sprite position towards the current touch
            m.pos = m.pos + vec2(CurrentTouch.x,CurrentTouch.y)
            end
        end
    end
    if CurrentTouch.state == ENDED then
     -- stop the sprite at its location
        m.pos = m.pos
    end
end

-- #Mine

Mine = class()

function Mine:init(x)
    -- you can accept and set parameters here
    self.pos = vec2(WIDTH/2, HEIGHT/2)
end

function Mine:draw()
    -- Codea does not automatically call this method
    sprite("Tyrian Remastered:Mine Spiked Huge",self.pos.x,self.pos.y)
end

function Mine:touched(touch)
    -- Codea does not automatically call this method
end

I don’t want the sprite to “jump” to the CurrentTouch. I want it to be drawn every frame moving towards the CurrentTouch. Thanks.

This isn’t exactly what you had in mind, but I think this example might help.

`
function setup()
x = 100
y = 100
end

function draw()
background(40, 40, 50)
strokeWidth(5)

ellipse(CurrentTouch.x, CurrentTouch.y, 10, 10)
sprite("Planet Cute:Character Boy", x, y)

local deltaX = 0
local deltaY = 0

if x < CurrentTouch.x then
    deltaX = 1
else
    deltaX = -1
end

if y < CurrentTouch.y then
    deltaY = 1
else
    deltaY = -1
end

x = x + deltaX
y = y + deltaY

end
`

To make the sprite stop animating you could add something like this:

if CurrentTouch.state == ENDED then return end

to just after the sprite()call.

I hope that helps

@LadyJane, take a look at the Bit Invader example. I think it does what you want.

Hi LadyJane,

Not very elegant but here is how I made it work:


-- #Main
displayMode(FULLSCREEN)
supportedOrientations(CurrentOrientation)

-- Use this function to perform your initial setup
function setup()
    m = Mine()
    mineStep = 1
    mineX = WIDTH/2
    mineY = HEIGHT/2
    targetX = mineX
    targetY = mineY
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(142, 88, 38, 255)
    mineTouch()
    -- Do your drawing here
    if targetX > mineX then mineX = mineX + mineStep end
    if targetX < mineX then mineX = mineX - mineStep end
    if targetY > mineY then mineY = mineY + mineStep end     
    if targetY < mineY then mineY = mineY - mineStep end
    Mine:draw(mineX, mineY)    
end

function mineTouch()
    if CurrentTouch.state == BEGAN or CurrentTouch.state == MOVING then
        targetX = CurrentTouch.x
        targetY = CurrentTouch.y           
    end
    if CurrentTouch.state == ENDED then
     -- stop the sprite at its location
        targetX = mineX
        targetY = mineY
    end
end

For the main.lua, And here is the Mine.lua.

-- #Mine

Mine = class()

function Mine:init(x)
    -- you can accept and set parameters here
    self.pos = vec2(WIDTH/2, HEIGHT/2)
end

function Mine:draw(x,y)
    -- Codea does not automatically call this method
    sprite("Tyrian Remastered:Mine Spiked Huge",x,y)
end

function Mine:touched(touch)
    -- Codea does not automatically call this method
end

Hope that helps.

Bri_G

:smiley:

@ladyjane


function setup()
    m = Mine()
end

function draw()
    background(142, 88, 38, 255)
    m:draw()   
end

function touched(t)
    m:touched(t)
end

Mine = class()

function Mine:init(x)
    self.pos = vec2(WIDTH/2, HEIGHT/2)
    self.velocity = vec2(0,0)
    self.speed = 4
end

function Mine:draw()
    self.pos = self.pos + self.velocity
    sprite("Tyrian Remastered:Mine Spiked Huge",self.pos.x,self.pos.y)
end

function Mine:touched(touch)
    if touch.state == ENDED then 
        self.velocity = vec2(0,0)
    else
        self.velocity = vec2(touch.x,touch.y) - self.pos
        self.velocity = self.velocity / self.velocity:len() * self.speed
    end
end

@Tao thanks. I was able to follow that example and get my sprite to move on the x-axis…

@ruilov I put in your code. That is what I was going for. Could you explain some things for me? Is velocity a built in code thing? What is happening in ~~~ self.velocity = self.velocity / self.velocity:len() * self.speed ~~~ also, I noticed when the sprite reached the place of touch, it would continue moving, and then come back to the touch point. But it never just stopped at the touch point. Do you know what causes that and how to make it stop at the touch point? Lastly, what is t in m:touched(t)?

Again, thanks for everyone’s help.

“Velocity” is not a built in thing - Ruilov defines it as a property of the “Mine” class so that every instance (object) of this class has its own velocity.

In Ruilov’s code, the sprite always has a velocity towards the touch point and this velocity always has the same magnitude. This leads to the overshoot: as the sprite gets really close to the touch point, it is still moving with the same speed and so goes past. It might stop if it ever landed exactly on the touch point, but Ruilov’s code contains a little problem if that ever happened because then it would be trying to divide by the length of a zero vector - not generally a good thing. So what would probably happen is that the code would stop running due to an error.

There are a variety of different ways to fix that. I prefer to have the object experience a force towards the touch point and then compute the acceleration and velocity from that - this means that if you change the touch point then it doesn’t instantly swing round and start moving towards it but rather undergoes a more realistic movement.

Another way is simply to take out the division by the length of the velocity (/ self.velocity:len()). Probably would want to adjust the speed a bit to tune the behaviour on that. Then it will get slower as it approaches the touch point.

re m:touched(t): instead of using CurrentTouch you can implement a special function called “touched” that gets called whenever a touch event happens (a touch starts or moves or ends). This is most useful if you want to handle multi touches (check out the multi touch example), but I also find cleaner than using CurrentTouch so it’s what I tend to use. Codea calls my touched(t) function and inside it I call m:touched.

The overshooting problem only happens if you keep your finger still on the screen, because then touch events stop being generated and self.velocity stays the same. If you leave your finger there long enough, the object will go off screen.

Perhaps better to keep track of the “target” where the object is trying to get to. Something like this: (I’m away from the ipad so this might not work):

function setup()
    m = Mine()
end

function draw()
    background(142, 88, 38, 255)
    m:draw()   
end

function touched(t)
    m:touched(t)
end

Mine = class()

function Mine:init(x)
    self.pos = vec2(WIDTH/2, HEIGHT/2)
    self.target= self.pos
    self.speed = 4
end

function Mine:draw()
    local move = self.target - self.pos
    if move:len() > self.speed then
        move = move / move:len() * self.speed
    end
    self.pos = self.pos + move
    sprite("Tyrian Remastered:Mine Spiked Huge",self.pos.x,self.pos.y)
end

function Mine:touched(touch)
    self.target = vec2(touch.x,touch.y)
end

Thanks Bri_G and ruilov. Both worked. The sprite moved towards the touch. They both are slightly different. Bri_G’s code stops the sprite as soon as the touch ends. ruilov’s sprite continues to move to the target touch even after the touch has ended. I like both of them, and will keep these as references. Again, thanks. Now on to collisions. :slight_smile:

One more request to ruilov (if you don’t mind). Could you explain WHY you used the math formula (I understand where the values are coming from) in the following:


Is this an algebra formula like rate * time = distance?

move is a vector, which describes a direction and a magnitude (length). Dividing it by it’s length (an operation called “normalizing”) gives you what’s known as a unit vector, which describes a direction with a length of 1 (in other words, it’s a pure direction). Multiplying the unit vector by your speed will cause it’s length to be equal to your speed, so if you were to add that to a point, that point would move in the direction the vector represents by the length of the vector.

Alternatively, he could have written that as:

move = move:normalize() * self.speed

Thanks toadkick.