Hello!
New user here, and absolutely in love with Pico-8!
Having followed several available tutorials online, I've cobbled together a few concepts to get this game running. Its just a player moving around a game field with some actors standing in the first screen. The player is repelled by the actors, which is sortof how I want to handle collision between the player and most objects. I may tweak that depending on what the player is bumping into.
I'm posting here because I've run into a dead end with my attempts to change the map displayed on screen. I intend to set triggers that switch what area is displayed on the screen (like the player walking onto a door tile), but currently I'm trying to figure out the necessary code to change what is displayed while the "trigger" is tied to btn4.
As you can see in the referenced WIP cartridge, scene_counter is set to "1" in _init, and if scene_counter == 1 then the map region (0,0,0,0) is drawn to the screen. However, if btnp(4) then the map region is changed to (16,0,0,0), which is the next region on the map data I'd like the "scene" to be framed at. With my current code the map background is changed correctly (and an empty desert scene is drawn, with marker-tiles on the outer corners to ensure I've set the new map coordinates correctly), however --
As you can see (if you run the game and press Z) the map is changed but the player appears to still be interacting with the constraints of the previous map scene on the island. Moving up and down, you still interact with the screen as if the two NPC actors are still present, and as if you are still on the island with the un-walkable water tiles and the yurt present in the center. You should don't see them.
I'm curious if anyone can review my code (and identify which tutorial I pulled the concepts from ;) ) and assist me in identifying why my game is showing the map correctly, but not actually moving the coordinates of where the action is taking place to the correct region in the map-editor data? Am I understanding whats happening correctly?
Any assistance would be great, and any constructive criticism would also be helpful. As a side note, it should be painfully obvious from my code that I'm a novice to any and all programming and could probably get done what I'm doing here with shorter code, likely with more advanced methods. I'm manually checking a lot of coordinates for my "collision detection", which works but is taking up more tokens that it probably should.
As an additional note, please ignore my in-code comments at the beginning. They're mostly just for me so I don't forget how the heck this all works, and more then a bit pretentious.
Thanks, and enjoy!
First off, finish whatever Lua tutorial you're following, you really need loops and tables.
Second, let's do a walk through of your code to see what's happening, starting in _init().
_init() looks good, the first cls() is redundant because it's called at the top of _draw(), other wise it's fine.
Things break down in _update. The big problem is that islandscene() is called every frame, when it only needs to be called once. Other functions, like waveshift() and gardenshift() should only be called when scene_counter is 1, probably the repel functions too. The playermovement() function should be moved up for clarity and precedence, generally speaking you want to handle the player movement before anything else so nothing occurs at the old location. Also, desertscene() is never called.
What ends up happening is that even though the desert is being drawn, all the coordinates the game is looking at take place on the island. To PICO-8 the map data is a big grid of sprite numbers, when you call the map function all the happens is those sprites are drawn on the screen. nothing else. map() isn't changing how PICO-8 interprets coordinates because it's not supposed to. Instead you have to use the camera() function and maintain your own offset into the game world.
The only other thing I want to point out is code formatting, yours is all over the place. The general rule is to indent one level when you enter a block, and dedent when you leave a block, example
function foo() -- top level code, no indent print("in foo") -- entered a block, add an indent if 3 < 4 then -- same indent level as above print("numbers still work right") -- entered another block, added another indent else -- goes on its own line the same level as the matching if, if I ever see you put another else at the end of a line I will hunt you down with an ax print("numbers are broken") -- same level as the print above end -- the end of the if started above, same level as that if and it's else end |
It may seem nit-picky but bad code formatting can hide bugs and make adding features harder, good code formatting can actually highlight some types of errors and make it more clear where to add things.
Thanks so much for the review. I am going through and updating to follow your suggestions. I make no claim to be experienced at all, and unfortunately these concepts are easier to learn if I actually do something with them, which is why tables is something I have yet to even attempt. I have a feeling that my enormous lists of coordinate checking can be rendered down into shorter, but more complicated/powerful functions if I understood what those were.
Forensically, the code as it is now shows what "work in progress" state it was when I posted it. Organizationally it is indeed all over the place, and for readability and precedence I will reorganize.
I noticed after submitting that desertscene() was never called, mostly because I had just stepped into utilizing that as a means of segregating the "state" of the game, or a "level", but had not actually segregated different values into those states. Luckily it doesn't seem to affect the issue I'm trying to resolve now.
I've since moved waveshift() and gardenshift() to only be called if scene_counter == 1 (thanks!), but now I'm curious how best to resolve what you mentioned about islandscene() being called every frame. Where should I move that to to ensure it isn't being redundantly processed? And do you think that has anything to do with the "persistence" of collision detection with islandscene()'s solid tiles? I'm beginning to think that thats exactly whats happening, and my attempt to change the "level" is simply drawing new area over the top of what is actually there.
With regards to your guidance on formatting, I can tell you exactly why its like that - I just moved from using the in-pico8 text editor to ATOM w/ plugins. Things look relatively uniform in ATOM, but after reviewing what it looks like in pico8 it really is illegible. My apologies for that, and props for spending the energy necessary to wade through the text!
Thanks again for your time.
Solved with help from /r/pico8!
The issue is that my "solid tile" checking function does not account for the coordinate changes when I change what section of the map is being displayed. So I created two new variables and included them in the tile checking calculation. Now it looks like this:
"function solid_tile(x,y)
local tile_x = ((x - (x % 8)) / 8) + levelpixeloffset_x
local tile_y = ((y - (y % 8)) / 8) + levelpixeloffset_y
if(fget(mget(tile_x, tile_y), 0)) then
return true else
return false
end
end
Its a simple (haha) matter of adjusting the values for levelpixeloffset_x and levelpixeloffset_y when changing the "level". Yes!
Seriously, double down on learning tables and loops. Tables are just collections of data, Lua makes it seem harder than it is, with tables seemingly doing so much, but just imagine a list or an address book. you stick a bunch of things in there, and you pull them out one at a time either by name or number. By analogue to other languages, people distinguish between tables with number indexes only, called lists or arrays, and tables with string indexes only, called dictionaries, objects, or just tables. This distinction helps a lot. You can even put tables in tables. for example, you can make mom and dad objects and stick them into a list, then with a for loop
for a in all(actors) do for i=-1,7 do if player.x - i > a.x and player.y - i < a.x + a.w and player.y - i > a.y and player.y - i < a.y + a.h then -- and so on end end end |
And there are several other places to add loops in your code.
Your "persistence issue" is because of scope. There are two kinds of scope that matter in Lua: Global and Local. Local variables are defined with the local keyword and the value given to the variable is erased when the function is over. The other kind is global. Global variables keep their value after the function ends. Any variable not marked as local is global so when you define the locations on the mom and dad objects, their locations simply never go anywhere and stick around for the player to bump into. The issue is that all the coordinates are in the same screen area still.
Like I said before, maintain on offset of what screen the player is on in the map and apply it to your calculations. There are a few ways to calc the offset and apply it, the trial and error of which works best for your code is best left up to you, it's really the joy of coding :)
Just remember, the computer does what you tell it to, not what you want it to do.
Thanks, codlark. I absolutely intend to continue my learning of Lua. I think pico-8 has made it accessible enough, and with enough immediate reward that I'll keep going.
edit:
looking back on your most recent code example, I think I may have a slight grasp on the concept. I really appreciate you writing code actually related to what I'm coding, so I can see the parallels. You're focusing on the redundancy of my "repel" NPC collision detection, which is the ugliest thing I've written so far. I will use what you're shown and make cleaning of this "repel" functionality my first foray into tables/etc.
Thanks again!
Codlark,
You are awesome.
Still reviewing your code.
Having viewed mine, and knowing what actor/object variables I'm using, what would you plug into where you've typed "actors" in "for a in all(actors) do" ? Can I list the actors/objects?
Just like its hard to write a novel if you never read any novels, it's hard to write a program if you don't read any programs. Take a look at some of the finished games on the BBS, under the player there's a button to display the code, learning things is as much a part of the process as writing the code.
Codlark,
Thanks for the thoughts. This absolutely is my learning experience playing out. I've always struggled to learn abstract concepts without actually doing some work with them, or creating something, or similar. Again, I really appreciate you reviewing and responding. I wouldn't be able to progress through this without the context-sensitive responses from peers.
As you can above, I've update and posted the newer version. Here's the code I'm working on to replace the ugly redundant area-checking for collision (repel):
function player_repel() for o in all(mom, dad, campfire) do for a = -1,7 do if player.x - a > o.x and player.y - a < o.x + o.w and player.y - a > o.y and player.y - a < o.y + o.h then if player.x < o.x then player.x -= 0.55 end if player.x > o.x then player.x += 0.55 end if player.y < o.y then player.y -= 0.55 end if player.y > o.y then player.y += 0.55 end end end end end |
This is basically 1:1 interpretation of what you'd sketched previously as an alternative to how I was dealing with checking each of the additional x, y coordinate areas that make up all the space on the 8x8 pixel sprites that make up the NPCs I'm trying to collide with / be repelled by. You'd made it pretty clear what needed to be done to translate mine into yours. As you can see in the new code, I've created objects for each actor (and a campfire), and each have related values/variables. Conveniently, I was able to simply transform each existing variable (eg. player_x became player.x). This is pretty much exactly what you'd suggested before sharing your code sketch, thanks!
As for my attempt to replicate your method with my variables, I appear to simply "not get it". I've spent the past several hours looking at examples and tutorials/write-ups on "for loops" and "tables" as well as objects in Lua. This is where I humbly reach out again for someone with experience. Hopefully you'll see that I've done some work and have taken your constructive criticism with the appropriate amount of seriousness!
As a note, Pico8 throws no errors, but none of the actors called in the player_repel() function's first For loop seem to be affected by the code, and having --'d out the lines where each actor's purpose-built repel function, no actors repel/collide anymore. At your convenience, and if you care to, can you review my code attempt and identify where I may be breaking conventions, or why it may not be working?
Hello,
I have looked at your code and I don't really understand what you are trying to do with all those collision checks.
First, the old ..._repel functions are not equivalent to your new repel function, that may explain why it doesn't work. (You can see that by comparing the conditions generated in the for loop and those in the old functions).
To be honest I could not find why exactly it doesn't work now, but I don't understand how it was working before !
I would advise trying to recode that collision checking from scratch, while keeping the nice loop :
for o in all(mom, dad, campfire) do ... |
The really strange part is this loop :
for a = -1,7 do |
That's 8 collision checks per update for each objects.
I am not a specialist but I don't understand why you are doing that.
What you are trying to seem to simply be square/square collision detection, you can looks here (clickable link) for an example of how it's usually done. It's not in Lua but I think you can adapt the code.
It's a random article I found with a quick search but it explains quite well.
Try simply moving the character back a fixed number of pixels when it collide with an object, with something as simple as what this article proposes. Then you can try to do something more fancy like repel force depending on the distance.
Good luck !
[Please log in to post a comment]