[SOLVED] I have a difficulty presenting my algorithm

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.
Post Reply
ilovelove2019
Citizen
Posts: 78
Joined: Wed Sep 11, 2019 10:38 am

[SOLVED] I have a difficulty presenting my algorithm

Post by ilovelove2019 »

Hello everyone, my game is gradually being improved, and now I am looking to add some enemies to my game. Specifically, some scorpion guards patrol. It could simply go back and forth, and I did it. But to make it more interesting, I want this scorpion to have some sleep, during which time the scorpion does not move nor does it cause damage to the player. And I dealt with the following two ways but failed:

1. I give the scorpion a variable 'self.state' which holds one of the 3 states' moveleft ',' moveright ',' idle 'and corresponding to those 3 states the scorpion will have velocity' self.vx 'are -25, 25 and 0. respectively. Then I declare a variable self.switchTime = 200. I reduce its value in the update function of' scorpion.lua ', if self.switchTime is greater than 0 I will keep reducing it, until it is less than or equal to 0 I will check the following: if the scorpion moves, let it sleep, and if the scorpion is sleeping, let it move. And I reset self.switchTime = 200 so this is checked continuously.

mycode: in scorpion.lua

Code: Select all

function scorpion:new()
 ...
 self.vx = 0
 self.state = 'moveleft'
 self.switchTime = 200
 ...
end

function scorpion:update(dt)
    if self.switchTime > 0 then
        self.switchTime = self.switchTime - 1
    else
        self.switchTime = 200
        print('a')
        if self.canMove then self.canMove = false end
        if self.canMove == false then self.canMove = true end
    end
    
    if self.canMove then
        if self.x <= self.targetX1 then
            self.state = 'moveright'
        elseif self.x >= self.targetX2 then
            self.state = 'moveleft'
        end
    elseif self.canMove == false then
        self.state = 'idle'
    end
    
    if self.state == 'idle' then
        self.vx = 0 
    elseif self.state == 'moveleft' then
        self.vx = -25
        self.scaleX = -1
    elseif self.state == 'moveright' then
        self.vx = 25
        self.scaleX = 1
    end
    
    if self.vx == 0 then
        
    else
        self.anim:update(dt)
    end
end
Oh forget, I also added print ('a') to check if the commands below worked, and the result was that the 'a' was still printed regularly, but the scorpion kept on moving. Turn left and then right. It did not have the rest period I wanted.

2. I try to use a timer library that pro has dedicated to the community (https://github.com/love2d-community/awe ... d#tweening). Specifically, I tried 'tick'. At first I tried this method before using method 1. I use the following statement:

Code: Select all

    tick.recur(
    function() 
        print("tick!") 
        if self.canMove == false then self.canMove = true elseif self.canMove == true then self.canMove == false end
    end, 3)
At this time, love reports that it cannot access the local variable self.canMove. (I put that statement in scorpion.lua but do not put it in scorpion: new () or scorpion: update (dt) or scorpion: draw ()). I thought in that tick.recur function I would run for i, v in ipairs (listOfScorpions) and check with v.canMove but I don't know if that works, because I haven't used it ever. , I don't know if it's true but I find it a little cheater :v. I want to manage everything in scorpion: update (dt), it looks more professional :v.

For the second way, for my inferiority, it's a little hopeless :v. I'm struggling with the first method. But the current problem is that it's not what I want, the scorpion doesn't rest, but it keeps moving.

Hope you all take some time to help me. Can you tell me where my code is wrong? Your opinion on ways 1 and 2, which is feasible and better? And I would be happy if you taught me a way of turning my theoretical wishes into a more realistic reality than my dump way. I want the enemy to move for a short time then rest before moving on. I know this is a fairly simple problem, but I know my skills are also poor and insufficient so that I cannot solve this problem myself. :(( Please sympathize with my bother. Please help me. Thank you so much.

Edit: In scorpion: update (dt) I realized the command had to fix from:

Code: Select all

if self.canMove then self.canMove = false end
        if self.canMove == false then self.canMove = true end
to:

Code: Select all

if self.canMove then self.canMove = false print('ok') 
        elseif self.canMove == false then self.canMove = true end
But the new problem arose and was not much different from the old one. At first the scorpion moved for a while, then it stood still and stood still. Hope for your help. I think I will need better algorithms from you.
Last edited by ilovelove2019 on Tue Nov 19, 2019 2:16 pm, edited 1 time in total.
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: I have a difficulty presenting my algorithm

Post by pgimeno »

One problem with the first idea is that when you change from "moveleft" to "idle", the scorpion loses all information about what direction it was moving, and there's no code to change the state back to "moveleft", because you forgot it, and because it has forgotten in what direction it was moving.

I'm not sure if you want the scorpion to pause only at the endpoints, or to pause along the path. The fix for the second case would be to get rid of canMove altogether, leaving the state as the authoritative source for whether it can move. Having one authoritative source of information always helps making the code more robust. Then, change 'idle' to 'idleleft' or 'idleright' depending on what direction it should resume to.

You have another problem there, by the way. You're counting frames, but different monitors can render at different frame rates (there are monitors that work at 100 Hz, for example), and then the timer would behave differently on different computers. To solve that, subtract dt instead of subtracting 1, and change the value to seconds (e.g. 3 seconds) instead of 200 frames.

I wrapped it in some code to make a main.lua that I could execute:

Code: Select all

local scorpion = {x=400, y=300, vy=0, anim = {update = function() end}}

function scorpion:new()
-- ...
 self.vx = 0
 self.state = 'moveleft'
 self.switchTime = 2.2
-- ...
end

function scorpion:update(dt)
    if self.switchTime > 0 then
        self.switchTime = self.switchTime - dt
    else
        self.switchTime = 2.2

        -- Handle state transitions at timer expiry
        if self.state == 'moveleft' then
            self.state = 'idleleft'
        elseif self.state == 'moveright' then
            self.state = 'idleright'
        elseif self.state == 'idleleft' then
            self.state = 'moveleft'
        else -- must be 'idleright' as it's the only remaining possibility
            self.state = 'moveright'
        end
    end

    -- Handle state transitions when hitting the end
    if self.state == 'moveleft' and self.x <= self.targetX1 then
      self.state = 'moveright'
    elseif self.state == 'moveright' and self.x >= self.targetX2 then
      self.state = 'moveleft'
    end

    if self.state == 'moveleft' then
        self.vx = -25
        self.scaleX = -1
    elseif self.state == 'moveright' then
        self.vx = 25
        self.scaleX = 1
    else -- must be one of the idle states
        self.vx = 0 
    end

    if self.vx ~= 0 then
        self.anim:update(dt)
    end
end

function love.load()
  scorpion:new()
  scorpion.canMove = true
  scorpion.targetX1 = 290
  scorpion.targetX2 = 550
end

function love.update(dt)
  scorpion:update(dt)
  scorpion.x = scorpion.x + scorpion.vx * dt
  scorpion.y = scorpion.y + scorpion.vy * dt
end

function love.draw()
  love.graphics.rectangle("fill", scorpion.x, scorpion.y, 10, 10)
  love.graphics.setLineStyle("rough")
  love.graphics.line(scorpion.targetX1 + 10, scorpion.y - 20, scorpion.targetX1 + 10, scorpion.y + 20)
  love.graphics.line(scorpion.targetX2 + 10, scorpion.y - 20, scorpion.targetX2 + 10, scorpion.y + 20)
end
If you want the idle moments to be at the endpoints, then the timer should only handle transitions from idle to moving, and the timer should be reset every time an endpoint is reached, as you change the state from moving to idle.

A timer library like tick normally helps you handle the timer itself, but it won't help fixing the algorithm. However, another approach is to use hump.timer, which has a nice function called timer.script, based on a coroutine. A coroutine is like a cooperative thread that switches task when you tell it to; in the case of timer.script, it switches task when you call the wait() function and switches back when the timer expires. Here's the same approach using hump.timer for the case of being idle only at the endpoints (the other case is a bit more complicated because it needs to asynchronously change from moving to idle and vice versa):

Code: Select all

local scorpion = {x=400, y=300, vy=0, anim = {update = function() end}}
local timer = require 'timer'

function scorpion:new()
-- ...
 self.vx = 0
 self.state = 'moveleft'
 self.switchTime = 2.2
 timer.script(function (wait)
    repeat
      wait(0) -- initial wait to let other parts initialize
      self.vx = -25
      while self.x >= self.targetX1 do
        wait(0) -- let the other code execute while we wait
      end
      self.vx = 0
      wait(self.switchTime)
      self.vx = 25
      while self.x <= self.targetX2 do
        wait(0) -- let the other code execute while we wait
      end
      self.vx = 0
      wait(self.switchTime)
    until false
  end)
-- ...
end

function scorpion:update(dt)
    if self.vx ~= 0 then
        self.anim:update(dt)
    end
end

function love.load()
  scorpion:new()
  scorpion.canMove = true
  scorpion.targetX1 = 290
  scorpion.targetX2 = 550
end

function love.update(dt)
  timer.update(dt)
  scorpion:update(dt)
  scorpion.x = scorpion.x + scorpion.vx * dt
  scorpion.y = scorpion.y + scorpion.vy * dt
end

function love.draw()
  love.graphics.rectangle("fill", scorpion.x, scorpion.y, 10, 10)
  love.graphics.setLineStyle("rough")
  love.graphics.line(scorpion.targetX1, scorpion.y - 20, scorpion.targetX1, scorpion.y + 20)
  love.graphics.line(scorpion.targetX2 + 10, scorpion.y - 20, scorpion.targetX2 + 10, scorpion.y + 20)
end
As you can see. timer.script() lets you write the script of how the enemy should move, where it should pause, etc. There's this other library that adds many more functions apart from wait(): https://love2d.org/forums/viewtopic.php?f=5&t=87592
ilovelove2019
Citizen
Posts: 78
Joined: Wed Sep 11, 2019 10:38 am

Re: I have a difficulty presenting my algorithm

Post by ilovelove2019 »

Woahhhh! Amazing!!! This time, I'm really really happy to receive your help. Your solution is very satisfactory. You have explained very clearly and properly of a pro, I have learned a lot from this time. Your code looks professional and I can feel its power (thanks to reliable information). Thank you and thank you very much. Besides, is there any solution to adjust travel and vacation time? For example, walk 5 seconds, rest 2 seconds, or break 3 seconds walk 2 seconds? This is just a question I am wondering more, if you are too busy then you may not need answers. Anyway, thank you very much, to be honest.
User avatar
pgimeno
Party member
Posts: 3550
Joined: Sun Oct 18, 2015 2:58 pm

Re: I have a difficulty presenting my algorithm

Post by pgimeno »

ilovelove2019 wrote: Tue Nov 19, 2019 1:59 pmBesides, is there any solution to adjust travel and vacation time? For example, walk 5 seconds, rest 2 seconds, or break 3 seconds walk 2 seconds?
You mean to make them different? Sure, just adjust the timer to the desired value when setting the new state, instead of at the beginning. You can even set it to a random time if you want. If that's not what you mean then please explain.
ilovelove2019
Citizen
Posts: 78
Joined: Wed Sep 11, 2019 10:38 am

Re: I have a difficulty presenting my algorithm

Post by ilovelove2019 »

Right! That is what I mean. Thank you for everything :)). I'm having fun with my scorpion :)) I'm very very fun now ;))
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 125 guests