Pixel-Walker v2.0: Testers Needed!

Make games! Discuss those games here.

Moderators: Bob the Hamster, marionline, SDHawk

TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

I honestly overlooked which thread I was replying to! I forgot to say how wonderful it is to have these scripts. I'm impressed in general and how you can push multiple NPCs at once based on weight. They are pretty complicated, maybe not easy for most people to extend. I'm going to write my own experience of using them.
This gives me ideas for a lot of things that I want to fix or improve on the engine's side, to make these scripts work better or make improvements possible.

Yes, either you want to resume the old path from where you left off, or re-path towards the new destination, and in either case can reconstruct the path. For Blood Ledger it would be sufficient to just remember the location when pathing was interrupted, and the path segment (destination).
But it does give me ideas for more path commands: one to compute a path and append (or prepend) a new path onto the existing one, so that you can build up a path that passes through a series of waypoints. For that to actually be useful, there would also have be commands to tell an NPC or hero to follow a computed path (currently, pathfinding NPCs/heroes always recompute the path every step). But that's far from necessary, probably not worth adding right now.

What if we have just one set of path commands, instead of duplicating them for heroes and npcs? NPC references are < 0, hero caterpillar ranks are >= 0. Though that means that you wouldn't be able to use NPC IDs, unlike every other command that operates on NPCs.

I think maybe the x/y positions should be in pixels to future-proof it.
These pathing commands don't have anyting to do with "move slice with wallchecking".

Regarding diagonal walls and diamonds, I confused myself. I should have said taking a shortcut of 20 pixels, not 10 pixels. And forget about octagons for the moment.
The idea is that if there's a wall at * which can be corner-cut around, then you could move diagonally from any of A/B/C/D to any of the others except the opposite one.

Code: Select all

 A
B*C
 D
This would be very useful for allowing movement around small obstructing objects on the map.
A diagonal wall wouldn't allow that. It would allow A to B and C to D but not A to C and B to D; or vice versa.

This is fine for tile-based movement, but how does that translate to pixel-based (e.g. "move slice with wallchecking")? It's like a superposition of two diagonal walls. It's not actually a diamond, but more like a point at the center of the tile - except that would let you overlap the tile by 10px, which shouldn't be allowed. An ordinary diagonal wall has no trouble translating to pixel-based movement.
User avatar
kylekrack
Liquid Metal Slime
Posts: 1240
Joined: Mon Jun 16, 2014 8:58 am
Location: USA
Contact:

Post by kylekrack »

A lot of this talk about pathfinding is going rightly over my head. I trust the two of you to make something great in due time. The concept of appending paths sounds incredible, though. Waypoints would be an awesome addition for making the paths more organic. Often the most objectively efficient path is not the most human path, as Bena touched on.

(Thank you tmc for your kind words)

In other news, an update for Pixel-Walker: foemaps!

Because I can't find any plotscripting commands for reading foemap data from maps directly, I had to use zones in their stead. That part is insignificant, the more important aspect is converting grid "steps" to pixel "steps." As is, it Pixel-Walker counts any frame on which the player has a non-zero velocity as a step. The battle countdown functions otherwise identically, according to the dictionary's explanation under "get battle countdown."
Attachments
Beware the sand! (countdown displayed bottom-left)
Beware the sand! (countdown displayed bottom-left)
pixel-walker0013.gif (1.13 MiB) Viewed 2775 times
My pronouns are they/them
Ps. I love my wife
lennyhome
Slime Knight
Posts: 115
Joined: Fri Feb 14, 2020 6:07 am

Post by lennyhome »

If I can give my 2 cents, I've tried it and it's surprisingly nice. As somebody who has dealt with physics and collisions before, this is my favorite part:

Code: Select all

# Clamp diagonal speed
if&#40;horiz <> 0 && vert <> 0&#41; then&#40;
  diag &#58;= &#40;3*spd&#41; / 4
  if&#40;diag < 1&#41; then&#40;diag &#58;= 1&#41;
  x &#58;= clampSpeed&#40;x, diag&#41;
  y &#58;= clampSpeed&#40;y, diag&#41;
&#41;
It's so wrong, yet it's accurate to within 5%. There's absolutely nothing not to love about it and 640k of RAM and 8 directions of movement ought to be enough for anyone.

Which brings me to my main point. To help projects like this one, it would be useful if the VM, in addition to shift operators also included some fixed point math functions. I'm thinking about "Fixed point math routines" as described in the Allegro 4 manual. Especially fixmul and fixdiv would be key.

One can do wonderful stuff with those functions. Some could be emulated right now with scripts, but the whole point is that they have to be fast to be useful.
Last edited by lennyhome on Thu Apr 02, 2020 10:19 pm, edited 2 times in total.
User avatar
kylekrack
Liquid Metal Slime
Posts: 1240
Joined: Mon Jun 16, 2014 8:58 am
Location: USA
Contact:

Post by kylekrack »

I had to make diagonal movement speed slowed somehow, since moving... 40%(?) faster with combined horizontal and vertical movement feels bad. I care more about things feeling decent than being perfect. Just clamping the speed to 75% the object's top speed was easiest here. It gets the effect, so I'd rather spend my time polishing other things. Plus, these scripts are open to modification. If a user wants to change the way diagonal speeds are handled, they're more than welcome to.

Anyway, I have no idea what the Allegro 4 manual is. I don't remember what fixed point math is. Haven't taken a comp sci class that covered that in years. Needless to say, I don't know how they relate. What's the "correct" way to deal with diagonal speed boosts?

EDIT: God, I'm reading about fixed point arithmetic and having flashbacks to discrete math class. *shudder*
Last edited by kylekrack on Fri Apr 03, 2020 12:26 am, edited 1 time in total.
My pronouns are they/them
Ps. I love my wife
lennyhome
Slime Knight
Posts: 115
Joined: Fri Feb 14, 2020 6:07 am

Post by lennyhome »

I don't remember what fixed point math is ... I don't know how they relate.
Suppose you want your object to accelerate by 0.5 pixels/s^2. I'm not saying you did anything wrong, it's just that you don't have much choice of parameters because of the integer arithmetic.
What's the "correct" way to deal with diagonal speed boosts?
Pythagora's theorem.
I have no idea what the Allegro 4 manual is.
It's a library that used to be popular during the golden age of dos games. Anything more recent uses floating-point numbers, but they're not supported by the scripting VM. Support for fixed-point numbers could be patched in and it would allow games such as billiard or minigolf.

----

Because I'm still under quarantine and I have nothing better to do, I've written a very minimal 24.8 fixed point library:

Code: Select all

script, fix24&#58;from, a
    return&#40;a * 256&#41;
end
script, fix24&#58;mul, a, b
    return&#40;multdiv&#40;a, b, 256&#41;&#41;
end
script, fix24&#58;div, a, b
    return&#40;multdiv&#40;a, 256, b&#41;&#41;
end
script, fix24&#58;to, a
    return&#40;a / 256&#41;
end
The problem is that [s]it's prone to overflow[/s] it's legit now, but due to the limited screen coordinates, it works and it may be useful. I'll publish a demo later but in the meantime, this is a minimal inertia-like setup:

Code: Select all

# starting position
blpx = fix24&#58;from&#40;100&#41;
blpy = fix24&#58;from&#40;100&#41;

while&#40;true&#41; do &#40;

# read the input or do a collision check 
# to modify the acceleraton

# apply acceleraton
blvx += blax, blvy += blay

# apply velocity
blpx += blvx, blpy += blvy

# apply friction
blvx = fix24&#58;mul&#40;blvx, 240&#41;
blvy = fix24&#58;mul&#40;blvy, 240&#41;

put slice&#40;chandle, fix24&#58;to&#40;blpx&#41;, fix24&#58;to&#40;blpy&#41;&#41;

wait&#40;1&#41;
&#41;
It's enough to play soccer with a slice. The point is that the code above can move objects at fractional speeds and speeds less than 1. A fix16 library would be much more precise, but this is what's feasible now.
Last edited by lennyhome on Sat Apr 04, 2020 12:39 am, edited 6 times in total.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Kylekrack has been posting previews of some great new features in discord!

There already is a multdiv function (it's not a math function), which is basically what you want. multdiv avoids overflow of intermediate result, and rounds the result (which is usually what you want). It isn't documented in the plotdictionary out of laziness because I didn't expect anyone would find it useful, but I see I was wrong, and I should add it.

Code: Select all

649,multdiv,3,0,0,0           # calculate int&#40;float&#40;a&#41;*b/c&#41;, with rounding and clamping &#40;undocumented&#41;
For example fix24:mul(1000, 10000) would overflow but you could rewrite it as multdiv(1000, 10000, 256).
96% of the time performance is irrelevant, but if you actually cared about it, the overhead of calling a script call is relatively huge* so you wouldn't want to use wrapper scripts. This is one reason why I want to add macros to HS.


* Trivia: in the early DOS days, every time a script was called it was loaded from disk... and DOS doesn't have a file cache.
Last edited by TMC on Sat Apr 04, 2020 12:22 am, edited 1 time in total.
lennyhome
Slime Knight
Posts: 115
Joined: Fri Feb 14, 2020 6:07 am

Post by lennyhome »

Code: Select all

the overhead of calling a script call is relatively huge
I'm never going to do physics on more than one object for now, also because of the lack of arrays. But it could be enough for a minigolf.

----

So, there's a little bit of a "pick your poison" type of situation with the "multdiv" function because the conversion to float causes a loss in precision and the rounding causes objects to never stop sliding.

If possible that function should be modified to use an int64 as intermediate type and use no rounding. It's nothing dramatic anyway, but with it you need an extra check for when the object is declared to be at rest.
Last edited by lennyhome on Sat Apr 04, 2020 1:34 am, edited 5 times in total.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

The script interpreter isn't terribly slow. On a very small set of benchmarks (testgame/benchmark.py and benchmark.rpg), it's 1-3x slower than CPython 2, and sometimes faster than CPython 3.
So, there's a little bit of a "pick your poison" type of situation with the "multdiv" function because the conversion to float causes a loss in precision and the rounding causes objects to never stop sliding.
There you go, thinking like a C programmer again. Here, "float" doesn't mean single precision, it means double precision. (No-one uses singles for intermediate results!) Now of course a double only has 53 bits of precision, but the return value has only 32 bits, so that's enough.

I don't see why rounding wouldn't be desired in the large majority of cases or why it causes a problem for your script specifically
lennyhome
Slime Knight
Posts: 115
Joined: Fri Feb 14, 2020 6:07 am

Post by lennyhome »

Code: Select all

why it causes a problem
Because your "multdiv" function says that 80% of 1 is 1. Your divide operator instead says that 80% of 1 is 0. It's not a problem, it's just that truncates towards zero, which in this particular case happens to be the right thing to do.

However, your division operator can't be used in place of an arithmetic shift. Consider this in Python3:

Code: Select all

>>> 1 // 2
0
>>> -1 // 2
-1
The normal behavior for integer division is to truncate toward negative infinity like floor for floats. The reason is that (x / 2) is supposed to be the same as (x >> 1). Your division operator returns 0 for -1 / 2.

Aren't integer numbers amazing?
User avatar
kylekrack
Liquid Metal Slime
Posts: 1240
Joined: Mon Jun 16, 2014 8:58 am
Location: USA
Contact:

Post by kylekrack »

Well, I'm thoroughly lost! I'd love to get fractional speeds in there, but I'm going to address getting a fully functional script set before returning to the physics. They work so adequately, for now, that'd it'd be a silly prioritization to rework them at the moment. That may be closer to the present than I thought, though, as I've added a lot of features in Pixel-Walker 2.3 that I was dreading.

On that note, here are some of those gifs tmc mentioned. Some of these things have apparent bugs (while others have thus far hidden ones), but typically either have workarounds or can be disabled.

The "talkbox" that collides with NPC slices and determines interactability now has independent width and height and will change dimensions to align with player direction. When facing diagonally, the width and height are averaged, making the talkbox a square. This way, it doesn't favor the x or y axis.

You can set a separate spriteset and frame count for hero/NPC idle animation. Additional frames beyond the initial idle animation frame are sequential in the sprite editor. Eg. an NPC that has a 3-frame idle animation that starts at spriteset 11 will go: Set 11 > Set 12 > Set 13 > Set 11 > ... The spriteset can be set to -1 for heroes/NPCs you don't want to idle.

Chase and Avoid NPC behaviors now differentiate between meandering and direct. "Meandering" in this case means that NPCs have a 25% chance of choosing a random direction instead of the direction towards the player.

Pushability can now be set to Full, Vertical, Horizontal, Up only, etc. This may not behave entirely as intended due to a lack of friction with hero-NPC collision. This will be fixed eventually. Basically, you can push a Vertical pushable NPC from the side by moving into it horizontally, and then moving diagonally (up or down).

Caterpillar party members was probably the most challenging, and presented the most problems. It works now, if you never rearrange the party. However, adding heroes is finicky, and swapping heroes in the Order and Team builtin menus *will* cause problems in hero sprites/animations. Looking into a solution, but it may require changes to the engine to truly fix.
Attachments
Caterpillar party members!
Caterpillar party members!
pixel-walker0024.gif (1.87 MiB) Viewed 2631 times
Pushability bits
Pushability bits
pixel-walker0020.gif (2.35 MiB) Viewed 2631 times
&amp;quot;Meandering&amp;quot; chase AI
&quot;Meandering&quot; chase AI
pixel-walker0019.gif (552.85 KiB) Viewed 2631 times
Multiframe, directional Idle Animations
Multiframe, directional Idle Animations
pixel-walker0017.gif (928.66 KiB) Viewed 2631 times
Non-square Talkbox
Non-square Talkbox
pixel-walker0016.gif (1.21 MiB) Viewed 2631 times
My pronouns are they/them
Ps. I love my wife
User avatar
Bob the Hamster
Lord of the Slimes
Posts: 7658
Joined: Tue Oct 16, 2007 2:34 pm
Location: Hamster Republic (Ontario Enclave)
Contact:

Post by Bob the Hamster »

Caterpillar party is beautiful! Great job!

I like the talk box visualizations too
User avatar
SwordPlay
Chemical Slime
Posts: 966
Joined: Sun Jan 22, 2017 9:32 am
Location: London, England
Contact:

Post by SwordPlay »

Great stuff! I like all the new features. *looks at extended idle animations longingly*
User avatar
kylekrack
Liquid Metal Slime
Posts: 1240
Joined: Mon Jun 16, 2014 8:58 am
Location: USA
Contact:

Post by kylekrack »

Making frames of animation sequential spritesets has actually worked out quite elegantly. I'm sure it makes adding frames to existing sprites an absolute pain, but it's not impossible. It effectively makes framecount limitless, without doing any wackiness like the standard 3-frame walking script.

Wait... could the 3-frame walking script be extended just by adding more sets and looping through them via modulo, rather than a simple toggle..?

I digress. Point is, the walkabout sets are 8-frames long, which makes 8-directional sets quite straightforward. That being said, part of me is dreading when the OHR gets support for custom framecounts and this all becomes obsolete.

EDIT: Bob, your words are kind and very meaningful. Caterpillar mode was hard but I'm very happy I powered through it.
Last edited by kylekrack on Thu Apr 09, 2020 10:11 am, edited 1 time in total.
My pronouns are they/them
Ps. I love my wife
User avatar
kylekrack
Liquid Metal Slime
Posts: 1240
Joined: Mon Jun 16, 2014 8:58 am
Location: USA
Contact:

Post by kylekrack »

Version 2.3 is up for download:
https://www.slimesalad.com/forum/viewto ... 697#130697

After battling with proper NPC collision for 2 straight days, I decided I should checkout back to my master branch and release v2.3 as is. Collisions are, to put it lightly, an absolute waking nightmare. I made progress that was so buggy, I'm not even sure it could be considered progress, since the code is so broken that it'll likely need to be scrapped and written from scratch anyway. Perhaps it will be a learning experience. I've learned that I'm better off pestering TMC to add slice collisions to existing wallchecking script commands. In all seriousness, I'm not going to attempt proper NPC collisions on my own, beyond what's already in the scripts, without someone's direct help.

All of the gifs in my previous post should cover most of what's new in this version. The other details worth mentioning are some fairly nasty bugs that I need to address before these scripts can be considered safe to use.
My pronouns are they/them
Ps. I love my wife
lennyhome
Slime Knight
Posts: 115
Joined: Fri Feb 14, 2020 6:07 am

Post by lennyhome »

kylekrack wrote:Collisions are ... an absolute waking nightmare.
In between feeling really bad because of this quarantine, I've written some slice-pass block collision code that works with the simple physics we've discussed earlier. This is the idea:

Code: Select all

variable&#40;pbx, pby, pbs&#41;
pbx = fix24&#58;to&#40;blpx&#41; / 20
pby = fix24&#58;to&#40;blpy&#41; / 20
pbs = read pass block&#40;pbx, pby&#41;

variable&#40;pb1x, pb1y, pb2x, pb2y, pbd, pbdx, pbdy&#41;
pb1x = fix24&#58;from&#40;pbx * 20&#41;
pb1y = fix24&#58;from&#40;pby * 20&#41;
pb2x = fix24&#58;from&#40;&#40;pbx + 1&#41; * 20&#41;
pb2y = fix24&#58;from&#40;&#40;pby + 1&#41; * 20&#41;

# north wall
if &#40;pbs & bit&#58;lshift&#40;1, 0&#41;&#41; then &#40;
	pbd = blpy - pb1y
	if &#40;pbd <= blrad&#41; then &#40;
		blpy += blrad - pbd
		blvy = -fix24&#58;mul&#40;blvy, 50&#41;
	&#41;
&#41;
...
There are 8 cases you need to check in total after the north wall case. 4 walls and 4 corners need to be checked if you want to do it properly. So it gets rather long. But it can be done if you want to.
Last edited by lennyhome on Mon Apr 20, 2020 6:40 pm, edited 2 times in total.
Post Reply