Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Touch and Go

edited May 2012 in Questions Posts: 41

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.

Tagged:

Comments

  • Posts: 141

    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

  • TaoTao
    Posts: 20

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

  • Posts: 447

    @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
  • Posts: 41

    @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.

  • Posts: 2,161

    "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.

  • Posts: 1,272

    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

    :-D

  • Posts: 447

    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
    
  • Posts: 41

    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. :)

  • Posts: 41

    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:

    ~move = move / move:len() * self.speed~

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

  • edited May 2012 Posts: 580

    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
    
  • Posts: 41

    Thanks toadkick.

Sign In or Register to comment.