Jump to content
IGNORED

Seeking Game Design/Development Advice. Yet Another WIP


Recommended Posts

Good Afternoon,

 

Experienced developers or generally smart people- I’m looking for game development guidance…

 

Having dragged a couple of projects across the finish line to the point that they could arguably be called games- confusing, crappy games with major loose ends and no polish, I’m trying a different approach this time.

 

My most common issues:   I get confused and/or lost during development.  And while its probably not obvious, I try to avoid asking for help from the forum.  So I’m approaching this one from a different direction and asking for design/process/method type help.  (And the usual technical too)

 

This one is like Bejeweled (but made exclusively with the finest Princess cut stones- No pretentious Briolette or Ceylon BS here ha ha!). No one has made this yet- right?  I made this basic kernel over the weekend.  Feeling like I should avoid pouring effort (and lots of code to confuse myself) into the graphics at this point.

 

So, specifically, can you folks please tell me:

1.       What steps would you have taken prior to making the kernel?

2.       What do you think about my current code?  Disorganized? Wrong? Missing something, could have done better, etc. 

2.       What would you suggest I do next? (broadly or specifically)

 

2020-06-08.thumb.png.499dee819e17c9197c0902a181b9405f.png
 

Thanks in advance,

Jeff

BBlocks.asm

  • Like 1
Link to comment
Share on other sites

I like what's going on here. :D

 

I noticed the game is 2K.  Are you planning on limiting the game to 2K?

I love well-done 2K games, they have their own charming simplicity.  I am actually currently working on a 2K yahtzee/farkle game, and by design I am limiting it to 2K.  Coding 2K games can be fun, because you can focus more on polish.

 

I have actually been thinking of doing a bejeweled game as well for a month or so.  My plans mainly concerned the kernel.  I have been trying to work out how to use the players/missiles to draw higher resolution jewel sprites, similar to Space Invaders.  I don't think this is really possible for anything over 6 sprites wide, so your approach with simple squares seems to be the only solution, outside of DCP+.

 

I saw a potential issue in the InitSystem portion of code.  You are reading the first byte of RAM to initialize your RNG, but first you do a EOR #1 to prevent a value of zero.  However, if $80's starting value is #1, the eor flips it to #0.  If you pull up the debugger, set $80 to 01, and set the program counter to F800, every jewel is set to red.  A simple fix could be to add a BNE before the EOR, skipping it unless $80 actually was zero.  (I see MLdB mentioned this as well.)

Also, I don't think it is reliable to use RAM for RNG initialization.  Some consoles/setups work half-okay, others don't work at all.  Here is a thread about RAM startup values.  I have heard INTIM is the best option for this.

 

About what to work on next, I would suggest deciding how to implement a cursor.  One thing you could do is simply flash the selected block.  You could either flash it on and off, or cycle the luminosity for the classic pulse effect.

Link to comment
Share on other sites

Hmm, you guys are right.  I changed it to ora #1 for now which gives 128 possible seeds I think.  Thanks! I suppose I'll change it to INTIM..  on the list.

 

JeremiahK.. 

 

I have a bare bones code template that I made that happens to be 2K..  I usually end up going to 4K.  I've been considering making this game 2K but really haven't decided.  My sense is that it would fit rather easily- only one playfield, pretty repetitive game.

 

I wrote off trying to use different shapes pretty early on here, feeling like the square blocks have a nice clean look.  Even if it were achievable, the Atarified graphics might actually bring it down a notch in my mind.  Just having big pretty-colored squares reminds me a little of a Lite-Brite.  Or Christmas lights.  Of course I'll likely have graphics above and below the main playfield where its easy to do.

 

OK..  I'll do the cursor next.  Having a pulse or flash is an obvious top contender- looks good, won't use kernel time outside the black bands where its tight.  Another thing I've been thinking of is crosshairs.  The ball is still available so I could use it in combination with COLUBK.  I was also thinking that fat, faint crosshairs (same height and width of a block) could be interesting and again- be taken care of in the black bands.

 

Thanks for the advice!

-Jeff

Link to comment
Share on other sites

Since no one had any advice on anything that I should have done first, I made one up.  Borrowing a little from Franklin Covey, I made a grid with tasks.  When something pops into my head, it will go on the grid.  How critical is it?  How difficult is it?  That will determine the order.  Often, difficult tasks are not important and will crowd out important tasks.  (Thanks Franklin Covey cult leaders!) The goal here is to set the clutter aside, focus on #1 tasks (top left) of which aren't very many when you slice it up, then #2, etc

Untitled.thumb.jpg.95178c1238e14f6c2afe44f68d8e4311.jpg

 

I also did a little of work on the code, not much, just a blinking cursor. I need a second cursor type, one that indicates that you have selected that block.  Probably, instead of blinking with black, it will blink with white.

 

Attached.

 

BBlocks.asm

Untitled.jpg

  • Like 1
Link to comment
Share on other sites

Another option is to implement the move in such a way that a selection isn't neccessary. Use the joystick to move the cursor over the block you want to move, hold fire, and move the joystick in the direction you want to switch.

You could process all this in the joystick code. When a directional input is detected, the fire button's state determines whether to move the cursor, or switch the blocks.

Edited by JeremiahK
fix typo
Link to comment
Share on other sites

I can work on centering the game board, I have been thinking about ways to do that.  For program control, I am thinking it might be best to store game state info in RAM, such as attract mode, gameplay mode, gameover/screensaver mode.  These bits are checked at the start of vertical blanking, and the code jumps to the correct logic routine.  It might be possible to use one universal kernel, like a classic 2K game.

 

Also, I see the object positions are being set the complicated way.  Since they don't move, I can hard-code it to save a lot of logic time and some ROM as well.  In fact, if we never need to move the objects, we can move the positioning routine to the init code, set and forget, and not waste any logic time at all.  This would go along with centering the display, so I can tackle all of that today.

 

A thought I had about cursors, with potential for a 2-player mode later, would be a direct on-off flash for player 1, and a pulsing light-to-dark effect for player 2.  Could also be selectable with the difficulty switches (or game modes), if for some reason you may want the cursors to look exactly the same, kind of like "invisible tanks".

Edited by JeremiahK
Link to comment
Share on other sites

I like the button-direction idea.  Very unique.

 

Will your centering involve asymmetrical playfield stores?

 

Yeah I think game state could be done with one byte or less and is the way to go.

 

Yes I used the standard object positioning but until the end, who knows if we'll need to position other objects, so it may end up in there anyway, then the custom code will be a waste.

Link to comment
Share on other sites

Sounds good, I will adjust the obj's Xpos in RAM.  We can keep that in mind for later if we need a little extra space.

2 hours ago, Just Jeff said:

Will your centering involve asymmetrical playfield stores?

I think I can use copy mode, and if I do it right, I won't need to write to the playfield every line, just set COLUPF to black after the last PF block is drawn to hide the copies.

Link to comment
Share on other sites

One thing we need to do is restructure how the board data is stored in RAM.  As it is, we use a whole byte for every block, which holds the color of the block.  This makes it easy to draw the kernel, but it hogs half the available RAM, plus the temp buffer for inside the display.

Since we only need to display 8 colors (7+1 to include black/blank slots), we can store each block color in exactly 3 bits.  Add another bit for marking matches, and we can fit exactly 2 blocks into 1 byte of RAM, freeing 32 bytes.

In order to display the board, in the black bars between rows, we can use the 3-bit values to index into the color table, and load them into the color buffer.  Then do a cursor check to modify the value for cursor display.

I think the best way to organize the bits would be like:  %mMcccCCC  where m=1st block's match bit, M=2nd block's match bit, c=1st block's color index, C=2nd block's color index.  This way, the match bits can be easily accessed after a BIT instruction.

Link to comment
Share on other sites

Looks doable, but do you think we'll need the RAM?  My instinct is to keep it as is why I'm doing the match code.  Its complicated enough for me as is.  (Often I think this incremental approach contributes to my rarely finishing though, so maybe I'm wrong)  Do you want to do the match code instead of me?

 

Also not sure about using the kernel time.  There is plenty now, but those black bands are going to shrink vertically as we try to fit scoring and a title or something above and below, and surely other code will end up in there as well (I envision COLUBK  and blocks fanfare maybe, for example.)  Though..  I think this is less of an issue, and potentially not an issue at all.  Gotta love black kernel bands.

 

I've attached my latest .bin (which I have not pushed).  You can move the cursor now with the joystick.  It won't display on the top line yet though (Though it is there).  Due to placement, the code for displaying it doesn't run for the top line yet.  I haven't figured out how to post (or even view) a complete .asm anymore so its not here :)

main.asm.bin

Link to comment
Share on other sites

Did you find a bug in the board layout in RAM?  I think it's causing the issue with the cursor not going to the top row.  Maybe I am misunderstanding how it works, but to center the board, I had to rearrange the order of player/missile/playfield objects.  I wanted to make sure it was still displaying everything in the right order, so I used the debugger to slowly overwrite the board RAM with 0's to see which byte of RAM corresponds to which block in the grid.  Everything was fine horizontally, but the vertical order seemed wrong.

If you write 00 to $80, the top left block turns black.  If you do $81, the bottom left block turns black.  $81 is the next block up, and so on up the grid.  Somehow the layout is not being set up correctly.  I will have a look at it and see if I can fix it.

 

I think leaving the RAM is fine, we shouldn't need the 32 bytes (probably?).  I wanted to mention it now, because it will be more difficult to add this in later if we need to.

I don't know how your match code is looking, add a branch to master for it and push the code to it, if you have already started on it.  We can probably work on it together.

Link to comment
Share on other sites

I thought of an algorithm for checking the matches:

 

Matches are only checked when the game enters "match mode", which is triggered when a move is made.

Loop through all the rows.  Step through each block in the current row, starting at the 2nd one.  Compare each block to the previous one, and if they are the same, increment a counter to keep track of the run.

If they are not the same, or if the end of the row is reached, check the run counter.  If it is a run of 3 or more, step back through the row, setting the match bits on the matching blocks.  Reset the run counter, and finish the row.

Then do the same thing for the columns.

 

I doubt there will be enough time to do this all in one frame, so the work can be split up across multiple frames.  Use a RAM counter to keep track of which logic frame we are on.  Checking 1-2 rows and columns per frame should suffice.

 

After the matches are found, the next step is taken.  Maybe play a color animation to show the matches flash quickly, then remove the blocks, add to the score, and switch to "gravity mode".

If no matches are found (an illegal move was made), move the blocks back, and go back to normal/move-making mode.

 

In gravity mode, the highest black block, or hole, in each column is found.  All blocks above the hole are moved down, and a random block is spawned at the top.  After all holes are filled, switch back to move-making mode.

Link to comment
Share on other sites

I don't think it will be possible to do any fancy gravity effects of blocks falling through the black bars between rows.  I can't think of a way to do this without splitting the entire kernel up into 4-line sections, which would greatly complicate everything.

Link to comment
Share on other sites

Yeah I wrote off the falling animation pretty early on as well. (Maybe at the end, but probably not)  Flashing the blocks for a couple of frames is perfect I think.

 

I also thought up something pretty similar to your gravity mode but its different- and I think its a decent replacement for the falling that we aren't doing. 

 

Short description is this-  On every frame (no mode necessary) A subroutine searches the grid from the bottom up for black.  When it finds one, it searches straight up until it finds a color.  When it finds a color, it moves it to the bottom spot (or maybe down one row only which would slow the animation if desired).  Then the routine quits until the next frame so it doesn't take too long, and thereby creates a bit of animation as well over the series of frames.  Joystick and match checks do get disabled when black is found..  maybe that's similar to your gravity mode.

 

 

Link to comment
Share on other sites

I just wrote a quick gravity routine!  It handles every column in the same frame, but only takes 1 step per frame, so we can animate the fall over time.

Here is a BIN with all white blocks.  Use the debugger to change some to black and see what happens.  Keep in mind I am ignoring the top row issue, so the top row actually is the bottom.  Also, this is currently inserted into vblank, so it always runs every frame.  I can add a gravity mode to handle the animation/logic, but skip the whole routine when we don't need gravity.

BBlocks.bin Gravity.asm

Edited by JeremiahK
Link to comment
Share on other sites

I am planning out how the game states/logic should flow.  This is what I've got, let me know what you think.

 

Attract Mode:

Show game board with some fun colorful effects, just randomly changing colors for a placeholder (easter egg potential later).

The previous game's score(s) should be shown.  (Top for P1, bottom for P2?  We can always decide on this later.)

Pressing Game Reset or P1's fire button will start a new game with the same settings as the last game (or default settings on startup).

Pressing Game Select or P1's up/down will enter Game Select Mode.

 

Game Select Mode:

Pressing Game Select or P1's up/down changes the game mode number, which is displayed in place of the score.  If we end up wanting a Space Invaders style 112 game modes, this might not work well.

Pressing Game Reset or P1's fire button starts the game by entering Game Init Mode.

Waiting for some period of time without an input will preserve the selected game mode, but switch back to Attract Mode.

 

Game Init Mode:

All game variables are initialized here, level, score, cursor position, etc.  The game board is cleared, and the game enters Shuffle Mode.

 

Shuffle Mode:

Blocks are randomly spawned into the top of the grid, falling down until the grid is full.  They are spawned in such a way that no matches/runs of colors are formed.

Once the grid is full, switch to Play Mode.

 

Play Mode:

This is the "normal" mode of the game, where the cursor is drawn, and moves can be made.

If a player moves the joystick while holding the fire button, switch to Move Mode.  Otherwise, move the cursor.

 

Move Mode:

Here a move is attempted.  The gems being moved are swapped, and a bit in RAM is checked, the "undo" bit.  If it is set, switch to Play Mode, otherwise Match Mode.  This is because if a match isn't found (an illegal move was made), the move will have to be made again to swap the gems back.  So basically, there are two Move Modes, Attempt Move Mode, and Undo Move Mode.

 

Match Mode:

Every row and column is checked for runs of 3 or more same-colored gems.  If none are found, check the "recheck matches" bit.  If it is set, we have already taken care of the matches and gravity, so clear the bit and switch to Play Mode.  Otherwise, set the "undo" bit, and switch back to Move Mode.

If matches are found (there might be more than one in the case of chain reactions), every matching gem is removed from the board, and the game enters Gravity Mode.

 

Gravity Mode:

Each column is checked from the bottom up for empty holes.  When one is found, every block above it (including other holes) is moved down 1 step, and a new block is spawned at the top.

If no holes are found, set the "recheck matches" bit (can probably be the same as the "undo" bit), and switch back to Match Mode, since the new gem locations may have formed new matches.

 

As far as I know, this should cover everything in the game, except for knowing when the game is lost.  In order to do this, it needs to be somehow determined that there are no moves left.

Note that the game modes that change the state of the board will also take care of animating the moves.

Link to comment
Share on other sites

Latest bin attached below.  Now you can switch two tiles.  Hold down the fire button, then hit a joystick direction.  It will switch those two tiles.  No validation yet so you just go around switching tiles.

 

Summary looks good overall.  I'm inclined to make one player, one game version for now, fancy later though.  And I think we hammered out the Gravity mode a little differently- right?

 

Move Mode:  As I've written some of this, it has occurred to me that I have potentially valuable data when its done.  I know the coordinates of the two tiles that were switched.  This would reduce the Match Mode requirements from 16 rows and columns to 3 in this case- either 2 rows and one column, or 1 row and 2 columns if it could be passed to the routine.  The routine could return a true/false back to me so I know it has to be switched back. Maybe that could be worked in there somehow?  I dunno.  What do you think?  Doing all 16 rows and columns every time may be too simple to pass up though, and more importantly, if it does have to do all 16 sometimes, it might have to be able to do it all the time for timing purposes.  That said, I think I lean towards just checking it all, so long as I can get a yes or no back from it when I call.  I'd keep the coordinates while waiting for an answer.

 

.bin attached.

main.asm.bin

Link to comment
Share on other sites

 

7 hours ago, Just Jeff said:

I'm inclined to make one player, one game version for now, fancy later though.

Agreed, I only mention it because it might affect how we implement certain things.

 

7 hours ago, Just Jeff said:

Move Mode:  As I've written some of this, it has occurred to me that I have potentially valuable data when its done.  I know the coordinates of the two tiles that were switched.  This would reduce the Match Mode requirements from 16 rows and columns to 3 in this case- either 2 rows and one column, or 1 row and 2 columns if it could be passed to the routine.  The routine could return a true/false back to me so I know it has to be switched back. Maybe that could be worked in there somehow?  I dunno.  What do you think?  Doing all 16 rows and columns every time may be too simple to pass up though, and more importantly, if it does have to do all 16 sometimes, it might have to be able to do it all the time for timing purposes.  That said, I think I lean towards just checking it all, so long as I can get a yes or no back from it when I call.  I'd keep the coordinates while waiting for an answer.

I would recommend always checking all 16 rows/columns.  This way, we can use the same routine to check after spawning in new blocks.  You can only make 1 or 2 matches on a single move, but after gravity and random spawns, a chain reaction could start, and there would be no way of knowing where they are on the board without checking everything.

The coordinates may not need to be kept, simply the cursor position and move direction.

I made a comment on the commit, and I will copy it here in case anybody wants to respond:


 

Quote

 

I think the best way to implement the game modes overall is to have a single byte, GameMode, hold the value for the current mode. Then in vblank, it checks for all the possible values, and jumps to the correct logic routine.
As an example, when a move is detected (joystick move + fire press while in play mode), rather than jumping directly to the SwitchPieces routine, GameMode is set to the value for switch mode, save any important data in RAM (cursor position, move direction) and the rest of logic is skipped for the current frame. In the next frame, this new value in GameMode causes the piece-switching code to run. Example: Switch the pieces, but wait 4 frames before going to the match-checking mode. In this time, the pieces might be blacked out in the kernel, or fade to black, to add some simple animation to the display.
I didn't explain this well enough last night, but that was the intention for how the game modes would work in practice.

Let me know what you think about this approach! I think it would make the handling of everything much easier to manage, especially things like checking if the move was valid, and undoing an illegal move.

 

Edited by JeremiahK
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...