Author Topic: Sprite move adjustment to match limit  (Read 2660 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Sprite move adjustment to match limit
« on: November 23, 2020, 07:23:50 am »
So here's my simple little program which I've been playing around with for a bit tonight:

Code: QB64: [Select]
  1. TYPE SpriteSheetInfo
  2.     InUse AS INTEGER
  3.  
  4.  
  5. TYPE DirectionalInfo
  6.     Xoff AS INTEGER
  7.     Yoff AS INTEGER
  8.     NumOfAnimations AS INTEGER
  9.  
  10. TYPE CharacterSheetInfo
  11.     OnSheet AS INTEGER 'Which sheet does our character appear on
  12.     Xoff AS INTEGER 'What's the Xoffset to where the first frame appears?
  13.     Yoff AS INTEGER 'What's the Yoffset to where the first frame appears?
  14.     ScreenX AS _FLOAT 'Where does the character appear on the visible screen?
  15.     ScreenY AS _FLOAT
  16.     Facing AS INTEGER 'which direction are we facing?
  17.     Frame AS INTEGER 'which frame is currently showing?
  18.     Moving AS INTEGER 'is the character moving?
  19.     MoveDirection AS INTEGER 'Which direction are they moving
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26. DIM SHARED MaxSheetLimit AS INTEGER: MaxSheetLimit = 100 'default to a maximum of 100 sprite sheets being loaded at a time
  27. DIM SHARED MaxCharacterLimit AS INTEGER: MaxCharacterLimit = 255 'default to a maximum of 255 characters being tracked at a time
  28. REDIM SHARED SpriteSheet(1 TO MaxSheetLimit) AS SpriteSheetInfo
  29. REDIM SHARED Actor(1 TO MaxCharacterLimit) AS CharacterSheetInfo
  30.  
  31. CONST Up% = 8, Down% = 1, Left% = 2, Right% = 4
  32. CONST North% = 8, South% = 1, West% = 2, East% = 4
  33. CONST Limit## = 60
  34.  
  35.  
  36. SCREEN _NEWIMAGE(1200, 960, 32)
  37. _DELAY .25
  38. _DELAY .25
  39.  
  40.  
  41. MaxCharacterLimit = 10 'for my demo, all I need is a max of 10 characters.  The fewer, the faster we process them
  42.  
  43. MainSheet = LoadSpriteSheet("Actor1.png")
  44. Hero = SetActor(MainSheet, 0, 0)
  45. TwinSis = SetActor(MainSheet, 144, 0)
  46.  
  47. MoveActor Hero, 5, 5
  48. MoveActor TwinSis, 0, 3
  49.  
  50. FaceActor Hero, North
  51. FaceActor TwinSis, East
  52.  
  53.  
  54. CharacterSelected = Hero
  55.     CLS , SkyBlue
  56.     FOR x = 0 TO _WIDTH STEP 48: LINE (x, 0)-STEP(0, _HEIGHT), -1: NEXT
  57.     FOR y = 0 TO _HEIGHT STEP 48: LINE (0, y)-STEP(_WIDTH, 0), -1: NEXT
  58.  
  59.     k = _KEYHIT
  60.     IF _KEYDOWN(100304) OR _KEYDOWN(100305) THEN 'Select is down
  61.         IF Actor(CharacterSelected).Moving = 0 THEN 'If we're not moving, then we can change where we're looking
  62.             SELECT CASE k
  63.                 CASE 18432: FaceActor CharacterSelected, North 'up
  64.                 CASE 19200: FaceActor CharacterSelected, West 'left
  65.                 CASE 19712: FaceActor CharacterSelected, East 'right
  66.                 CASE 20480: FaceActor CharacterSelected, South 'south
  67.             END SELECT
  68.         END IF
  69.     ELSE
  70.         SELECT CASE k 'Just a normal, unmodified keypress
  71.             CASE 9 'tab
  72.                 IF CharacterSelected = Hero THEN CharacterSelected = TwinSis ELSE CharacterSelected = Hero
  73.             CASE 27 'esc
  74.                 SYSTEM
  75.             CASE 18432 'up
  76.                 IF Actor(CharacterSelected).ScreenY > 0 AND Actor(CharacterSelected).Moving = 0 THEN
  77.                     FaceActor CharacterSelected, North
  78.                     Actor(CharacterSelected).MoveDirection = Up
  79.                     Actor(CharacterSelected).Moving = 1
  80.                 END IF
  81.             CASE 19200 'left
  82.                 IF Actor(CharacterSelected).ScreenX > 0 AND Actor(CharacterSelected).Moving = 0 THEN
  83.                     FaceActor CharacterSelected, West
  84.                     Actor(CharacterSelected).MoveDirection = West
  85.                     Actor(CharacterSelected).Moving = 1
  86.                 END IF
  87.             CASE 19712 'right
  88.                 IF Actor(CharacterSelected).ScreenX < _WIDTH - 48 AND Actor(CharacterSelected).Moving = 0 THEN
  89.                     FaceActor CharacterSelected, East
  90.                     Actor(CharacterSelected).MoveDirection = East
  91.                     Actor(CharacterSelected).Moving = 1
  92.                 END IF
  93.             CASE 20480 'south
  94.                 IF Actor(CharacterSelected).ScreenY < _HEIGHT - 48 AND Actor(CharacterSelected).Moving = 0 THEN
  95.                     FaceActor CharacterSelected, South
  96.                     Actor(CharacterSelected).MoveDirection = South
  97.                     Actor(CharacterSelected).Moving = 1
  98.                 END IF
  99.         END SELECT
  100.     END IF
  101.     _KEYCLEAR
  102.  
  103.     DrawActors 0
  104.     _LIMIT Limit
  105.     _DISPLAY
  106.  
  107.  
  108. SUB DrawActors (Where AS INTEGER)
  109.     STATIC XChange AS _FLOAT, YChange AS _FLOAT, FrameChange AS _FLOAT
  110.     DIM move AS _FLOAT
  111.     move = 48 / (Limit / 3)
  112.     FOR i = 1 TO MaxCharacterLimit
  113.         IF Actor(i).OnSheet = 0 THEN _CONTINUE 'if it's not a valid actor, skip them
  114.         IF Actor(i).Moving THEN
  115.             SELECT CASE Actor(i).MoveDirection
  116.                 CASE Up: YChange = -move: XChange = 0
  117.                 CASE West: XChange = -move: YChange = 0
  118.                 CASE East: XChange = move: YChange = 0
  119.                 CASE South: YChange = move: XChange = 0
  120.             END SELECT
  121.             FrameChange = FrameChange + move
  122.             Actor(i).Frame = FrameChange MOD 3 'Which frame of our animation are we playing?
  123.             IF FrameChange > 48 THEN
  124.                 FrameChange = 0: Actor(i).Moving = 0 'once we've traveled 48 pixels, we're finished
  125.             ELSE
  126.                 Actor(i).ScreenY = Actor(i).ScreenY + YChange 'otherwise we move the proper amount across the screen
  127.                 Actor(i).ScreenX = Actor(i).ScreenX + XChange
  128.             END IF
  129.         ELSE
  130.             Actor(i).Frame = 0
  131.         END IF
  132.         Xoff = Actor(i).Xoff + 48 * Actor(i).Frame
  133.         Yoff = Actor(i).Yoff
  134.         SELECT CASE Actor(i).Facing
  135.             CASE 1: Yoff = Yoff
  136.             CASE 2: Yoff = Yoff + 48
  137.             CASE 4: Yoff = Yoff + 96
  138.             CASE 8: Yoff = Yoff + 144
  139.         END SELECT
  140.         _PUTIMAGE (Actor(i).ScreenX, Actor(i).ScreenY), Actor(i).OnSheet, Where, (Xoff, Yoff)-STEP(48, 48)
  141.     NEXT
  142.  
  143.  
  144. SUB MoveActor (Who AS INTEGER, WhereX AS INTEGER, WhereY AS INTEGER)
  145.     Actor(Who).ScreenX = WhereX * 48
  146.     Actor(Who).ScreenY = WhereY * 48
  147.  
  148. SUB FaceActor (Who AS INTEGER, WhichDirection AS INTEGER)
  149.     Actor(Who).Facing = WhichDirection
  150.  
  151.  
  152. FUNCTION SetActor (WhichSheet AS INTEGER, Xoff AS INTEGER, Yoff AS INTEGER)
  153.     IF WhichSheet = 0 THEN ERROR 5: EXIT SUB
  154.     IF SpriteSheet(WhichSheet).InUse = 0 THEN ERROR 5: EXIT SUB
  155.     'We'll get an ERROR 5 -- "Illegal Function Call" -- if we try and access a sheet that hasn't been loaded,
  156.     'freed, or is otherwise an invalid handle.
  157.  
  158.     FOR i = 1 TO MaxCharacterLimit
  159.         IF Actor(i).OnSheet = 0 THEN 'it's a free handle for the character/actor to use
  160.             Actor(i).OnSheet = SpriteSheet(WhichSheet).InUse
  161.             Actor(i).Xoff = Xoff
  162.             Actor(i).Yoff = Yoff
  163.             Actor(i).ScreenX = -1000 'we start off the screen, by default
  164.             Actor(i).ScreenY = -1000
  165.             Actor(i).Frame = 1
  166.             SetActor = i
  167.             EXIT SUB
  168.         END IF
  169.     NEXT
  170.  
  171.  
  172. FUNCTION LoadSpriteSheet (File AS STRING)
  173.     IF _FILEEXISTS(File) = 0 THEN ERROR 53: EXIT SUB 'Error 53 is "File Not Found"
  174.     temp = _LOADIMAGE(File, 32)
  175.     IF temp = -1 THEN ERROR 57: EXIT SUB 'Error 57 is "Device I/O Error".  Apparently we found the file
  176.     '                                     (or else we'd toss the error above), but it won't load properly.
  177.     '                                     This sounds like a Device I/O Error to me, and keeps us from having
  178.     '                                     issues trying to debug if the file exists, or the path is proper, by
  179.     '                                     tossing the same error message.
  180.  
  181.     'If we're to here, we now need to check for an open file handle, and then assign it for use.
  182.     FOR i = 1 TO MaxSheetLimit
  183.         IF SpriteSheet(i).InUse = 0 THEN
  184.             SpriteSheet(i).InUse = temp 'Our image handle, for where the sprite sheet is loaded at in memory
  185.             LoadSpriteSheet = i
  186.             EXIT SUB 'If all works as expected, we exit our sub here
  187.         END IF
  188.     NEXT
  189.     'If we made it to here, we have a problem -- there's no file handles available for usage.
  190.     'By default, I'm setting the MaxSheetLimit to 100, so as to make certain that we keep memory usage reasonable.
  191.     'Free up some unused sprite sheets, combine a few, or manually increase the limit yourself.
  192.     'Otherwise, you're going to see:
  193.     ERROR 67 'Error 67 is "Too Many Files"

Required is the sprite sheet below.

As it currently exists, it's working properly for me.  The issue comes when I change the CONST Limit to a non-multiple of 15 value.

Limit = 15 works.
Limit = 30 works.
Limit = 60 works.

Limit = 20.... NOPE!

The idea behind this is that we're supposed to move X number of pixels for Y number of cycles, until we've moved 48 pixels total.

So let's say we have a limit of 48... We should move 1 pixel per loop, until we move 48 times.
If the limit is 24, we should move 2 pixels per loop, until we move 24 times.
If the limit is 12, we should move 4 pixels per loop, until we move 12 times.

In each case, the goal is to end up moving 48 pixels, so we stay lined up with our grid.

Seems like a simple equation:  Move =  48 / Limit

Only issue with that is that I want to have our actors moving faster than that.  With the above, we only move 1 square per second with our hero.  I was wanting to adjust that so that he could move say 3 steps per second.

Move = 48 / (Limit /3)

And this is where I have my current little formula for my movement...

Which, oddly enough, works with a Limit of 15, 30, or 60...  But not with a limit of 20...



Anyone have a clue what the heck is wrong with my math here?  The idea seems simple enough -- bigger steps for a slower computer, so the game play stays the same -- but the movement isn't matching up at all.  Change that Limit to 20 and then move up or down the screen a couple lines, and you'll see that we don't stay in position as we should.  Each step, we lose (or gain) a little ground, until we're in the midst of the line and not centered inside the box at all.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Sprite move adjustment to match limit
« Reply #1 on: November 23, 2020, 07:45:44 am »
If I want to move 1 square per second, this works:

Code: QB64: [Select]
  1. TYPE SpriteSheetInfo
  2.     InUse AS INTEGER
  3.  
  4.  
  5. TYPE DirectionalInfo
  6.     Xoff AS INTEGER
  7.     Yoff AS INTEGER
  8.     NumOfAnimations AS INTEGER
  9.  
  10. TYPE CharacterSheetInfo
  11.     OnSheet AS INTEGER 'Which sheet does our character appear on
  12.     Xoff AS INTEGER 'What's the Xoffset to where the first frame appears?
  13.     Yoff AS INTEGER 'What's the Yoffset to where the first frame appears?
  14.     ScreenX AS _FLOAT 'Where does the character appear on the visible screen?
  15.     ScreenY AS _FLOAT
  16.     Facing AS INTEGER 'which direction are we facing?
  17.     Frame AS INTEGER 'which frame is currently showing?
  18.     Moving AS _FLOAT 'is the character moving?
  19.     MoveDirection AS INTEGER 'Which direction are they moving
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26. DIM SHARED MaxSheetLimit AS INTEGER: MaxSheetLimit = 100 'default to a maximum of 100 sprite sheets being loaded at a time
  27. DIM SHARED MaxCharacterLimit AS INTEGER: MaxCharacterLimit = 255 'default to a maximum of 255 characters being tracked at a time
  28. REDIM SHARED SpriteSheet(1 TO MaxSheetLimit) AS SpriteSheetInfo
  29. REDIM SHARED Actor(1 TO MaxCharacterLimit) AS CharacterSheetInfo
  30.  
  31. CONST Up% = 8, Down% = 1, Left% = 2, Right% = 4
  32. CONST North% = 8, South% = 1, West% = 2, East% = 4
  33. CONST Limit## = 60
  34.  
  35.  
  36. SCREEN _NEWIMAGE(1200, 960, 32)
  37. _DELAY .25
  38. _DELAY .25
  39.  
  40.  
  41. MaxCharacterLimit = 10 'for my demo, all I need is a max of 10 characters.  The fewer, the faster we process them
  42.  
  43. MainSheet = LoadSpriteSheet("Actor1.png")
  44. Hero = SetActor(MainSheet, 0, 0)
  45. TwinSis = SetActor(MainSheet, 144, 0)
  46.  
  47. MoveActor Hero, 5, 5
  48. MoveActor TwinSis, 0, 3
  49.  
  50. FaceActor Hero, North
  51. FaceActor TwinSis, East
  52.  
  53.  
  54. CharacterSelected = Hero
  55.     CLS , SkyBlue
  56.     FOR x = 0 TO _WIDTH STEP 48: LINE (x, 0)-STEP(0, _HEIGHT), -1: NEXT
  57.     FOR y = 0 TO _HEIGHT STEP 48: LINE (0, y)-STEP(_WIDTH, 0), -1: NEXT
  58.  
  59.     k = _KEYHIT
  60.     IF _KEYDOWN(100304) OR _KEYDOWN(100305) THEN 'Select is down
  61.         IF Actor(CharacterSelected).Moving = 0 THEN 'If we're not moving, then we can change where we're looking
  62.             SELECT CASE k
  63.                 CASE 18432: FaceActor CharacterSelected, North 'up
  64.                 CASE 19200: FaceActor CharacterSelected, West 'left
  65.                 CASE 19712: FaceActor CharacterSelected, East 'right
  66.                 CASE 20480: FaceActor CharacterSelected, South 'south
  67.             END SELECT
  68.         END IF
  69.     ELSE
  70.         SELECT CASE k 'Just a normal, unmodified keypress
  71.             CASE 9 'tab
  72.                 IF CharacterSelected = Hero THEN CharacterSelected = TwinSis ELSE CharacterSelected = Hero
  73.             CASE 27 'esc
  74.                 SYSTEM
  75.             CASE 18432 'up
  76.                 IF Actor(CharacterSelected).ScreenY > 0 AND Actor(CharacterSelected).Moving = 0 THEN
  77.                     FaceActor CharacterSelected, North
  78.                     Actor(CharacterSelected).MoveDirection = Up
  79.                     Actor(CharacterSelected).Moving = 1
  80.                 END IF
  81.             CASE 19200 'left
  82.                 IF Actor(CharacterSelected).ScreenX > 0 AND Actor(CharacterSelected).Moving = 0 THEN
  83.                     FaceActor CharacterSelected, West
  84.                     Actor(CharacterSelected).MoveDirection = West
  85.                     Actor(CharacterSelected).Moving = 1
  86.                 END IF
  87.             CASE 19712 'right
  88.                 IF Actor(CharacterSelected).ScreenX < _WIDTH - 48 AND Actor(CharacterSelected).Moving = 0 THEN
  89.                     FaceActor CharacterSelected, East
  90.                     Actor(CharacterSelected).MoveDirection = East
  91.                     Actor(CharacterSelected).Moving = 1
  92.                 END IF
  93.             CASE 20480 'south
  94.                 IF Actor(CharacterSelected).ScreenY < _HEIGHT - 48 AND Actor(CharacterSelected).Moving = 0 THEN
  95.                     FaceActor CharacterSelected, South
  96.                     Actor(CharacterSelected).MoveDirection = South
  97.                     Actor(CharacterSelected).Moving = 1
  98.                 END IF
  99.         END SELECT
  100.     END IF
  101.     _KEYCLEAR
  102.  
  103.     DrawActors 0
  104.     _LIMIT Limit
  105.     _DISPLAY
  106.  
  107.  
  108. SUB DrawActors (Where AS INTEGER)
  109.     STATIC XChange AS _FLOAT, YChange AS _FLOAT, FrameChange AS _FLOAT
  110.     DIM move AS _FLOAT
  111.     move = 48 / Limit
  112.     FOR i = 1 TO MaxCharacterLimit
  113.         IF Actor(i).OnSheet = 0 THEN _CONTINUE 'if it's not a valid actor, skip them
  114.         IF Actor(i).Moving THEN
  115.             SELECT CASE Actor(i).MoveDirection
  116.                 CASE Up: YChange = -move: XChange = 0
  117.                 CASE West: XChange = -move: YChange = 0
  118.                 CASE East: XChange = move: YChange = 0
  119.                 CASE South: YChange = move: XChange = 0
  120.             END SELECT
  121.             FrameChange = FrameChange + move
  122.             Actor(i).Frame = (FrameChange \ 8) MOD 3 'Which frame of our animation are we playing?
  123.             IF FrameChange > 48 THEN
  124.                 FrameChange = 0: Actor(i).Moving = 0 'once we've traveled 48 pixels, we're finished
  125.             ELSE
  126.                 Actor(i).ScreenY = Actor(i).ScreenY + YChange 'otherwise we move the proper amount across the screen
  127.                 Actor(i).ScreenX = Actor(i).ScreenX + XChange
  128.             END IF
  129.         ELSE
  130.             Actor(i).Frame = 0
  131.         END IF
  132.         Xoff = Actor(i).Xoff + 48 * Actor(i).Frame
  133.         Yoff = Actor(i).Yoff
  134.         SELECT CASE Actor(i).Facing
  135.             CASE 1: Yoff = Yoff
  136.             CASE 2: Yoff = Yoff + 48
  137.             CASE 4: Yoff = Yoff + 96
  138.             CASE 8: Yoff = Yoff + 144
  139.         END SELECT
  140.         _PUTIMAGE (Actor(i).ScreenX, Actor(i).ScreenY), Actor(i).OnSheet, Where, (Xoff, Yoff)-STEP(48, 48)
  141.     NEXT
  142.  
  143.  
  144. SUB MoveActor (Who AS INTEGER, WhereX AS INTEGER, WhereY AS INTEGER)
  145.     Actor(Who).ScreenX = WhereX * 48
  146.     Actor(Who).ScreenY = WhereY * 48
  147.  
  148. SUB FaceActor (Who AS INTEGER, WhichDirection AS INTEGER)
  149.     Actor(Who).Facing = WhichDirection
  150.  
  151.  
  152. FUNCTION SetActor (WhichSheet AS INTEGER, Xoff AS INTEGER, Yoff AS INTEGER)
  153.     IF WhichSheet = 0 THEN ERROR 5: EXIT SUB
  154.     IF SpriteSheet(WhichSheet).InUse = 0 THEN ERROR 5: EXIT SUB
  155.     'We'll get an ERROR 5 -- "Illegal Function Call" -- if we try and access a sheet that hasn't been loaded,
  156.     'freed, or is otherwise an invalid handle.
  157.  
  158.     FOR i = 1 TO MaxCharacterLimit
  159.         IF Actor(i).OnSheet = 0 THEN 'it's a free handle for the character/actor to use
  160.             Actor(i).OnSheet = SpriteSheet(WhichSheet).InUse
  161.             Actor(i).Xoff = Xoff
  162.             Actor(i).Yoff = Yoff
  163.             Actor(i).ScreenX = -1000 'we start off the screen, by default
  164.             Actor(i).ScreenY = -1000
  165.             Actor(i).Frame = 1
  166.             SetActor = i
  167.             EXIT SUB
  168.         END IF
  169.     NEXT
  170.  
  171.  
  172. FUNCTION LoadSpriteSheet (File AS STRING)
  173.     IF _FILEEXISTS(File) = 0 THEN ERROR 53: EXIT SUB 'Error 53 is "File Not Found"
  174.     temp = _LOADIMAGE(File, 32)
  175.     IF temp = -1 THEN ERROR 57: EXIT SUB 'Error 57 is "Device I/O Error".  Apparently we found the file
  176.     '                                     (or else we'd toss the error above), but it won't load properly.
  177.     '                                     This sounds like a Device I/O Error to me, and keeps us from having
  178.     '                                     issues trying to debug if the file exists, or the path is proper, by
  179.     '                                     tossing the same error message.
  180.  
  181.     'If we're to here, we now need to check for an open file handle, and then assign it for use.
  182.     FOR i = 1 TO MaxSheetLimit
  183.         IF SpriteSheet(i).InUse = 0 THEN
  184.             SpriteSheet(i).InUse = temp 'Our image handle, for where the sprite sheet is loaded at in memory
  185.             LoadSpriteSheet = i
  186.             EXIT SUB 'If all works as expected, we exit our sub here
  187.         END IF
  188.     NEXT
  189.     'If we made it to here, we have a problem -- there's no file handles available for usage.
  190.     'By default, I'm setting the MaxSheetLimit to 100, so as to make certain that we keep memory usage reasonable.
  191.     'Free up some unused sprite sheets, combine a few, or manually increase the limit yourself.
  192.     'Otherwise, you're going to see:
  193.     ERROR 67 'Error 67 is "Too Many Files"

The issue I'm having is when I try and make the little fellow move larger steps at a time.  One square per second works lovely, no matter what the limit is.  One squares per 1/3rd second?  NOPE!  We get off center every time.

I'm off for some coffee and donuts.  Maybe sugar and caffeine will make my problem magically go away.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Sprite move adjustment to match limit
« Reply #2 on: November 23, 2020, 10:24:19 am »
Morning Steve

This problem seems all-too-similar to a few I've looked at lately, and sprite sheets would be no oddball on the growing list - all of which are handled by parameterized motion. I may break off and do a dumb-simple sprite sheet demo if this isn't the approach you want, but I'm sure it'll work for general sprite-like purposes.

The basic idea is this:

Suppose I want to move the character from point A to point B at constant velocity. (We can add accelerations later once the whole model is done. Constant speed for now.)

Taking a one-dimensional case, this would mean the character starts at x=A, and then lands at x=B. (So far all this models your thing exactly I think.)

Now, I want an equation for the path of motion from A to B. I'm not thinking about how it's implemented yet, or _LIMIT, or STEPs, nothing. Just an equation that links me from A to B. To do this, I write:

x = A * (1 - p) + B * p

... where p is the so-called "parameter" that classifies the motion. It's limited between the values 0 and 1, inclusive. Set p=0 to result in x=A. Set p=1 to get x=B. Set 0<p<1 to get anywhere in between. To execute the path of motion you let p go from 0 to 1 in some kind of loop. The variable p of course has to step from 0 to 1, it can't be done continuously like nature does.

The key is this: whatever your refresh rate is, let the step in p equal the reciprocal of that number. For Z frames per second, your motion can be like:

Code: QB64: [Select]
  1. FOR p = 0 to 1 STEP 1/Z
  2.     x = A * (1 - p) + B * p

... and that's it.

« Last Edit: November 23, 2020, 10:41:20 am by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline MasterGy

  • Seasoned Forum Regular
  • Posts: 327
  • people lie, math never lies
    • View Profile
Re: Sprite move adjustment to match limit
« Reply #3 on: November 23, 2020, 10:46:19 am »
Hi ! the trouble comes from the fact that with your solution you want to do 10 in 3 steps (X = X + 10/3 looks like this: X = X + 0.3333)
Your 1 step will thus be 0.3333. in the second step 0.6666, and in the third step 0.9999 .You do not get 10 in the third step.

In the solution of STxAxTIC, current position = 10/3 * actual step. It's right.

Quick solution to your program: move = (48 / (INT (Limit / 3)))
« Last Edit: November 23, 2020, 10:48:41 am by MasterGy »

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Sprite move adjustment to match limit
« Reply #4 on: November 23, 2020, 12:05:05 pm »
Amazing demo, Steve! Nice characters, too, I like sexy little miss witch.

I did help someone get a sprite animation walking demo for somebody's homework. It had an animation for standing, running, and jumping seamless with multikey input. It looked like a promising start to a game, would've been nice to get it somewhere

https://www.qb64.org/forum/index.php?topic=1077.msg102772#msg102772

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Sprite move adjustment to match limit
« Reply #5 on: November 23, 2020, 02:03:55 pm »
Got my little glitch sorted out.  The issue was that I simply wasn't moving that final fractional distance, if our total ended up being more than 48 pixels.

For example:   Let's say I want to move 10 pixels, 3 steps at a time...

         IF FrameChange > 10 THEN
                FrameChange = 0: Actor(i).Moving = 0 'once we've traveled 48 pixels, we're finished
            ELSE
                Actor(i).ScreenY = Actor(i).ScreenY + YChange 'otherwise we move the proper amount across the screen
                Actor(i).ScreenX = Actor(i).ScreenX + XChange
            END IF

With the code like the above, I increase my movement by 3...6...9... inside the ELSE statement, and then I just simply toss out the remainder, since it'd be more than 10 pixels.  Unless the math was perfect, the movement failed.

My solution, and a little upgrade for testing purposes is the following:

Code: QB64: [Select]
  1. TYPE SpriteSheetInfo
  2.     InUse AS INTEGER
  3.  
  4.  
  5. TYPE DirectionalInfo
  6.     Xoff AS INTEGER
  7.     Yoff AS INTEGER
  8.     NumOfAnimations AS INTEGER
  9.  
  10. TYPE CharacterSheetInfo
  11.     OnSheet AS INTEGER 'Which sheet does our character appear on
  12.     Xoff AS INTEGER 'What's the Xoffset to where the first frame appears?
  13.     Yoff AS INTEGER 'What's the Yoffset to where the first frame appears?
  14.     ScreenX AS _FLOAT 'Where does the character appear on the visible screen?
  15.     ScreenY AS _FLOAT
  16.     LastX AS INTEGER 'Where was the character at last, on the map?
  17.     LastY AS INTEGER
  18.     MoveSpeed AS INTEGER 'How fast can this character move?
  19.     Facing AS INTEGER 'which direction are we facing?
  20.     Frame AS INTEGER 'which frame is currently showing?
  21.     Moving AS _FLOAT 'is the character moving?
  22.     MoveDirection AS INTEGER 'Which direction are they moving
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29. DIM SHARED MaxSheetLimit AS INTEGER: MaxSheetLimit = 100 'default to a maximum of 100 sprite sheets being loaded at a time
  30. DIM SHARED MaxCharacterLimit AS INTEGER: MaxCharacterLimit = 255 'default to a maximum of 255 characters being tracked at a time
  31. REDIM SHARED SpriteSheet(1 TO MaxSheetLimit) AS SpriteSheetInfo
  32. REDIM SHARED Actor(1 TO MaxCharacterLimit) AS CharacterSheetInfo
  33.  
  34. CONST Up% = 8, Down% = 1, Left% = 2, Right% = 4
  35. CONST North% = 8, South% = 1, West% = 2, East% = 4
  36. CONST Limit## = 60
  37.  
  38.  
  39. SCREEN _NEWIMAGE(1200, 960, 32)
  40. _DELAY .25
  41. _DELAY .25
  42.  
  43.  
  44. MaxCharacterLimit = 10 'for my demo, all I need is a max of 10 characters.  The fewer, the faster we process them
  45.  
  46. MainSheet = LoadSpriteSheet("Actor1.png")
  47. Hero = SetActor(MainSheet, 0, 0)
  48. TwinSis = SetActor(MainSheet, 144, 0)
  49.  
  50. MoveActor Hero, 5, 5
  51. MoveActor TwinSis, 0, 3
  52.  
  53. FaceActor Hero, North
  54. FaceActor TwinSis, East
  55. SetActorSpeed Hero, 10
  56.  
  57.  
  58. CharacterSelected = Hero
  59.     CLS , SkyBlue
  60.     FOR x = 0 TO _WIDTH STEP 48: LINE (x, 0)-STEP(0, _HEIGHT), -1: NEXT
  61.     FOR y = 0 TO _HEIGHT STEP 48: LINE (0, y)-STEP(_WIDTH, 0), -1: NEXT
  62.     k = _KEYHIT
  63.     IF _KEYDOWN(100304) OR _KEYDOWN(100305) THEN 'Select is down
  64.         IF Actor(CharacterSelected).Moving = 0 THEN 'If we're not moving, then we can change where we're looking
  65.             SELECT CASE k
  66.                 CASE 18432: FaceActor CharacterSelected, North 'up
  67.                 CASE 19200: FaceActor CharacterSelected, West 'left
  68.                 CASE 19712: FaceActor CharacterSelected, East 'right
  69.                 CASE 20480: FaceActor CharacterSelected, South 'south
  70.             END SELECT
  71.         END IF
  72.     ELSE
  73.         SELECT CASE k 'Just a normal, unmodified keypress
  74.             CASE 9 'tab
  75.                 IF CharacterSelected = Hero THEN CharacterSelected = TwinSis ELSE CharacterSelected = Hero
  76.             CASE 27 'esc
  77.                 SYSTEM
  78.             CASE 45
  79.                 Actor(CharacterSelected).MoveSpeed = Actor(CharacterSelected).MoveSpeed - 1
  80.             CASE 61
  81.                 Actor(CharacterSelected).MoveSpeed = Actor(CharacterSelected).MoveSpeed + 1
  82.             CASE 18432 'up
  83.                 IF Actor(CharacterSelected).ScreenY > 0 AND Actor(CharacterSelected).Moving = 0 THEN
  84.                     FaceActor CharacterSelected, North
  85.                     Actor(CharacterSelected).MoveDirection = Up
  86.                     Actor(CharacterSelected).Moving = 1
  87.                 END IF
  88.             CASE 19200 'left
  89.                 IF Actor(CharacterSelected).ScreenX > 0 AND Actor(CharacterSelected).Moving = 0 THEN
  90.                     FaceActor CharacterSelected, West
  91.                     Actor(CharacterSelected).MoveDirection = West
  92.                     Actor(CharacterSelected).Moving = 1
  93.                 END IF
  94.             CASE 19712 'right
  95.                 IF Actor(CharacterSelected).ScreenX < _WIDTH - 48 AND Actor(CharacterSelected).Moving = 0 THEN
  96.                     FaceActor CharacterSelected, East
  97.                     Actor(CharacterSelected).MoveDirection = East
  98.                     Actor(CharacterSelected).Moving = 1
  99.                 END IF
  100.             CASE 20480 'south
  101.                 IF Actor(CharacterSelected).ScreenY < _HEIGHT - 48 AND Actor(CharacterSelected).Moving = 0 THEN
  102.                     FaceActor CharacterSelected, South
  103.                     Actor(CharacterSelected).MoveDirection = South
  104.                     Actor(CharacterSelected).Moving = 1
  105.                 END IF
  106.         END SELECT
  107.     END IF
  108.     _KEYCLEAR
  109.  
  110.     DrawActors 0
  111.     _LIMIT Limit
  112.     LOCATE 1, 1: PRINT "Hero Speed:"; Actor(Hero).MoveSpeed
  113.     LOCATE 2, 1: PRINT "Twin Speed:"; Actor(TwinSis).MoveSpeed
  114.     _DISPLAY
  115.  
  116.  
  117. SUB DrawActors (Where AS INTEGER)
  118.     STATIC XChange AS _FLOAT, YChange AS _FLOAT, FrameChange AS _FLOAT
  119.     DIM move AS _FLOAT
  120.     FOR i = 1 TO MaxCharacterLimit
  121.         IF Actor(i).OnSheet = 0 THEN _CONTINUE 'if it's not a valid actor, skip them
  122.         IF Actor(i).Moving THEN
  123.             IF FrameChange = 0 THEN
  124.                 Actor(i).LastX = Actor(i).ScreenX
  125.                 Actor(i).LastY = Actor(i).ScreenY
  126.             END IF
  127.             move = 48 * Actor(i).MoveSpeed / Limit
  128.             IF move <= 0 THEN
  129.                 FrameChange = 0: Actor(i).Moving = 0 'we're too damn slow to move!
  130.             ELSE
  131.                 SELECT CASE Actor(i).MoveDirection
  132.                     CASE Up: YChange = -move: XChange = 0
  133.                     CASE West: XChange = -move: YChange = 0
  134.                     CASE East: XChange = move: YChange = 0
  135.                     CASE South: YChange = move: XChange = 0
  136.                 END SELECT
  137.                 FrameChange = FrameChange + move
  138.                 IF FrameChange >= 48 THEN
  139.                     Actor(i).Frame = 0
  140.                     FrameChange = 0: Actor(i).Moving = 0 'once we've traveled 48 pixels, we're finished
  141.                     Actor(i).ScreenX = Actor(i).LastX + SGN(XChange) * 48
  142.                     Actor(i).ScreenY = Actor(i).LastY + SGN(YChange) * 48
  143.                 ELSE
  144.                     Actor(i).Frame = (ABS(Actor(i).LastX - Actor(i).ScreenX) + ABS(Actor(i).LastY - Actor(i).ScreenY)) \ 16 'Which frame of our animation are we playing?
  145.                     Actor(i).ScreenY = Actor(i).ScreenY + YChange 'otherwise we move the proper amount across the screen
  146.                     Actor(i).ScreenX = Actor(i).ScreenX + XChange
  147.                 END IF
  148.             END IF
  149.         ELSE
  150.             Actor(i).Frame = 0
  151.         END IF
  152.         Xoff = Actor(i).Xoff + 48 * Actor(i).Frame
  153.         Yoff = Actor(i).Yoff
  154.         SELECT CASE Actor(i).Facing
  155.             CASE 1: Yoff = Yoff
  156.             CASE 2: Yoff = Yoff + 48
  157.             CASE 4: Yoff = Yoff + 96
  158.             CASE 8: Yoff = Yoff + 144
  159.         END SELECT
  160.         _PUTIMAGE (Actor(i).ScreenX, Actor(i).ScreenY), Actor(i).OnSheet, Where, (Xoff, Yoff)-STEP(48, 48)
  161.     NEXT
  162.  
  163.  
  164. SUB MoveActor (Who AS INTEGER, WhereX AS INTEGER, WhereY AS INTEGER)
  165.     Actor(Who).ScreenX = WhereX * 48
  166.     Actor(Who).ScreenY = WhereY * 48
  167.     Actor(Who).LastX = WhereX * 48
  168.     Actor(Who).LastY = WhereY * 48
  169.  
  170. SUB SetActorSpeed (Who AS INTEGER, HowFast AS INTEGER)
  171.     Actor(Who).MoveSpeed = HowFast
  172.  
  173.  
  174. SUB FaceActor (Who AS INTEGER, WhichDirection AS INTEGER)
  175.     Actor(Who).Facing = WhichDirection
  176.  
  177.  
  178. FUNCTION SetActor (WhichSheet AS INTEGER, Xoff AS INTEGER, Yoff AS INTEGER)
  179.     IF WhichSheet = 0 THEN ERROR 5: EXIT SUB
  180.     IF SpriteSheet(WhichSheet).InUse = 0 THEN ERROR 5: EXIT SUB
  181.     'We'll get an ERROR 5 -- "Illegal Function Call" -- if we try and access a sheet that hasn't been loaded,
  182.     'freed, or is otherwise an invalid handle.
  183.  
  184.     FOR i = 1 TO MaxCharacterLimit
  185.         IF Actor(i).OnSheet = 0 THEN 'it's a free handle for the character/actor to use
  186.             Actor(i).OnSheet = SpriteSheet(WhichSheet).InUse
  187.             Actor(i).Xoff = Xoff
  188.             Actor(i).Yoff = Yoff
  189.             Actor(i).ScreenX = -1000 'we start off the screen, by default
  190.             Actor(i).ScreenY = -1000
  191.             Actor(i).LastX = -1000 'we start off the screen, by default
  192.             Actor(i).LastY = -1000
  193.             Actor(i).MoveSpeed = 1
  194.             Actor(i).Frame = 1
  195.             SetActor = i
  196.             EXIT SUB
  197.         END IF
  198.     NEXT
  199.  
  200.  
  201. FUNCTION LoadSpriteSheet (File AS STRING)
  202.     IF _FILEEXISTS(File) = 0 THEN ERROR 53: EXIT SUB 'Error 53 is "File Not Found"
  203.     temp = _LOADIMAGE(File, 32)
  204.     IF temp = -1 THEN ERROR 57: EXIT SUB 'Error 57 is "Device I/O Error".  Apparently we found the file
  205.     '                                     (or else we'd toss the error above), but it won't load properly.
  206.     '                                     This sounds like a Device I/O Error to me, and keeps us from having
  207.     '                                     issues trying to debug if the file exists, or the path is proper, by
  208.     '                                     tossing the same error message.
  209.  
  210.     'If we're to here, we now need to check for an open file handle, and then assign it for use.
  211.     FOR i = 1 TO MaxSheetLimit
  212.         IF SpriteSheet(i).InUse = 0 THEN
  213.             SpriteSheet(i).InUse = temp 'Our image handle, for where the sprite sheet is loaded at in memory
  214.             LoadSpriteSheet = i
  215.             EXIT SUB 'If all works as expected, we exit our sub here
  216.         END IF
  217.     NEXT
  218.     'If we made it to here, we have a problem -- there's no file handles available for usage.
  219.     'By default, I'm setting the MaxSheetLimit to 100, so as to make certain that we keep memory usage reasonable.
  220.     'Free up some unused sprite sheets, combine a few, or manually increase the limit yourself.
  221.     'Otherwise, you're going to see:
  222.     ERROR 67 'Error 67 is "Too Many Files"

We can now set an actor's speed, and, for testing purposes, we can manually alter the speed to whatever we want it to be.  Limit shouldn't have any affect on movement now (within reason -- a limit of 1 frame per second means our hero will never move more than 1 frame in a second, as we won't read key presses for more than that), and you can change the hero and his twin separately to have fun testing them out. 

No collision is detected, nor anything else at the moment, but what we have here is a basic, bare-bones animation movement routine, which I hope to keep expanding upon for in the future.  :)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline MasterGy

  • Seasoned Forum Regular
  • Posts: 327
  • people lie, math never lies
    • View Profile
Re: Sprite move adjustment to match limit
« Reply #6 on: November 23, 2020, 02:19:16 pm »
I like the game, I'm glad you solved it! I look forward to the end!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Sprite move adjustment to match limit
« Reply #7 on: November 24, 2020, 06:20:38 am »
I like the game, I'm glad you solved it! I look forward to the end!

I'm finally getting around to character movement style routines for The Echoes of Destiny -- my little fantasy RPG which I've been working on forever and ever and ever, and which might be finished in 2025, if I live that long...

The idea is rather simple at first glance, but in the end, it seems like I'm writing my own programming language here.  My initial goal, when I started working on this project, was to create a game engine, where all the important scripting and information was all contained inside the map data itself.

For example, here's the first page, or so, of the beginning map for the game:

Quote
[SGS (Steve's Gaming System)]

[HEADER]
Version: 0.1
For: The Echoes of Destiny
Asset Type: Map
Name: Beginner's Dungeon
[END HEADER]

[MAP INFO]
Background Music: "Creepy Crypt.mp3"
Battle BG Image: "Crypt 001.png"
Size: 10 x 10
Random Encouter Chance: 02
[END MAP INFO]

[ENCOUNTER TABLE]
Roll: 1 TO 12
Result: 1 TO 10, "Brittle Skeleton"
Result: 11 TO 12, "Old Skeleton"
[END ENCOUNTER TABLE]

[START UP SCRIPT]
PlaceActor Hero, 5, 5
Play BGM
SetScreen Black
Wait 3.0
SetScreen FadeIn Normal
Message "Ugg...  Where am I.  My head hurts.", Wait For Input ("Press <ANY KEY> to continue...")
Message "I can't remember harly anything.  Do I even know my own name?", Wait For Input ("Yes", "No")
IF Input IS "Yes" THEN
   Run Name Dialog
ELSE
   Message "Wow!  I can't even remember my own name!", Wait For Input ("Press <ANY KEY> to continue...")
   SetActorName Hero, "Jane Doe"
END IF
Message "I guess I'll be known as [HERO 01 NAME] from now on!", Wait for 3.0
Message "But where the heck am I?", Wait for 3.0
Message "Am... Am I sleeping in a coffin?", Wait for 3.0
Play "Bang and Clang.mp3"
PlaceActor Hero, 5, 4
Message "AHH!! I was in a coffin!", Wait for 1.0
SetScreen FadeIn Black
Wait 3.0
SetScreen FadeIn Normal
Message "What the heck just happened?  Did I just faint?", Wait For Input ("Press <ANY KEY> to continue...")

I don't think you guys need to see the whole map datafile (it's still a work in progress), but there's enough there so you can get a concept of how things work.  Basically, I'm writing my own gaming script, and my "game" itself won't be anything more than the engine which processes that script.  The above is basically the start of the "Wake Up and Introduction" script which places our hero in the world, and the Beginner's Dungeon helps highlight how the character moves and interacts with the game world.

You start out naked (character in their underwear, so as to not offend folks terribly), sleeping in a cold and uncaring coffin, in a crypt , in the middle of nowhere.  Once you make it out of the first room, you'll realize you were buried in the bottom of a dusty old dungeon/prison, and from the looks of things nobody has been here in ages!  The next map (Beginner's Dungeon 02) is larger and has a greater chance for random encounters and exploration, so the player can get a feeling for how to battle and equip gear and all.  Once it's navigated, the next map is the outside world, with a small village located not far from there...

...And that's where the hero begins their real journey into figuring out what happened to them, why it happened, and has to decide what to do from this point on.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!