|
I made a game called civiliz8n click here to play I'm pretty happy with it so far, I might expand on it a bit by improving the art/adding a splash screen/high score board? Anyway, play my game and make a game for octojam folks! Tann fucked around with this message at 23:54 on Oct 5, 2015 |
# ? Oct 5, 2015 23:52 |
|
|
# ? Apr 25, 2024 13:52 |
|
ExiledTinkerer posted:The Gameduino Project guy did a (porting and such) thing apparently: Aw, and I was hoping I'd be the first to do this. I still wanna do this, of course.
|
# ? Oct 6, 2015 13:26 |
|
Tann posted:I made a game called civiliz8n click here to play This looks cool, will give it a play when I get home.
|
# ? Oct 6, 2015 20:06 |
|
Making progress on my octojam game Staying without superchip or XO chip for this jam, since I wont have time to make them justice. Limitations are good for me. Buffis fucked around with this message at 21:30 on Oct 7, 2015 |
# ? Oct 7, 2015 21:24 |
|
That's really cool buffis. The bullet hell shooter works so well with chip8's pixel perfect collision detection!
|
# ? Oct 7, 2015 23:59 |
|
http://octojam.com/octojam-ii/games/civiliz8n Just submitted my first game to the jam site. Still 20 days left people, make a game!
|
# ? Oct 11, 2015 01:27 |
|
Octo deserves to be pretty. I've just put the finishing touches on an Octo syntax package for the Atom text editor. https://github.com/james0x0A/language-octo
|
# ? Oct 11, 2015 18:30 |
|
My music player now has a dedicated edit mode. Press 7 to enter and 8 to advance ticks. Edit: I made a title screen. http://johnearnest.github.io/Octo/index.html?gist=5fd018baae22420d208b TomR fucked around with this message at 19:21 on Oct 15, 2015 |
# ? Oct 15, 2015 18:05 |
|
So I'm trying to do a game for the jam but I have no idea how to read and store values into RAM. Help I think that load, save and unpack have something to do with it but I have no idea how to use them.
|
# ? Oct 18, 2015 21:12 |
|
To load, you do something like this:code:
To save: code:
|
# ? Oct 18, 2015 21:56 |
|
Dr. Stab posted:To load, you do something like this: Thanks Dr. Stab, you're the coolest.
|
# ? Oct 19, 2015 03:42 |
|
Dr. Stab's explanation is perfect. For the record, :unpack is for more subtle and less common situations. Basically it's a convenient syntax for saying "take the two bytes that would represent an instruction like i := NNN and store it in the registers v0 and v1." You can then write the instruction into memory with save v1. A detailed scenario where you might want to use self-modifying code like this is described in this example and the techniques guide section on multiple indirection. It's not necessary to use a feature like this in most programs, but it was helpful in making Cave Explorer's code more straightforward.
|
# ? Oct 19, 2015 05:26 |
|
Making progress on danm8ku Playable work-in-progress here: http://octojam.com/octojam-ii/games/danm8ku
|
# ? Oct 19, 2015 21:11 |
|
Nice, I love the variance in movement speeds!
|
# ? Oct 19, 2015 22:57 |
|
There's still easily time to make a game for octojam. I'm just starting to think about my second game. Join the fun!
|
# ? Oct 20, 2015 23:00 |
|
Is there any way to inspect RAM during debugging? edit: managed to fix my code without it but it would still be cool to have a RAM viewer in the debug mode. Am I too dumb to find it or there's nothing like that implemented? Explosive Tampons fucked around with this message at 05:30 on Oct 23, 2015 |
# ? Oct 23, 2015 05:06 |
|
Currently there isn't a RAM inspector, but several people are already tinkering with it. In the meantime, remember that you can always pause execution, drop into the debugger and then directly use your browser's Javascript console to inspect or modify any part of the emulator state. It's a little clumsy, but very flexible.
|
# ? Oct 23, 2015 05:44 |
|
Time for more tales of software archaeology. In the process of disassembling my large corpus of old Chip8 ROMs, I noticed a strange pattern. Quite a few programs would start with a forward jump over a chunk of random-seeming data. Static analysis showed that the data this jump avoided was never read, written or executed. What could it be? I wrote a little program which searched for this pattern and then attempted to display the contents of the blob as a sequence of ASCII characters. Bingo. On the left is a rom title, and on the right is the string I found: code:
|
# ? Oct 23, 2015 06:12 |
|
Do you think this could have been supported/displayed somewhere on old chip8 emulators? It seems curious that so many ROMs would embed data an identical fashion.
|
# ? Oct 23, 2015 16:08 |
|
I made a quick thing yesterday, it looks pretty sweet. http://johnearnest.github.io/Octo/index.html?gist=2b767e2162f73f6e7652 ( https://github.com/buffis/misc-samples/blob/master/Octo/patterns.8o )
|
# ? Oct 26, 2015 09:34 |
|
Buffis posted:I made a quick thing yesterday, it looks pretty sweet. This program made me go cross-eyed for a second and gave me morgellons.
|
# ? Oct 26, 2015 09:43 |
|
Hey if anyone wants to make a game, Octojam is still open for another 5 seconds.
|
# ? Nov 1, 2015 08:00 |
|
Does anyone have questions about how Eaty The Alien works?
|
# ? Nov 4, 2015 06:30 |
|
I'd enjoy hearing some more about the game design overall, but in particular the challenges you faced and the solutions you came up with for making a (relatively?) balanced layout of items (and the data structure used) for this type of game - how have things changed (if any) from eg Cave Explorer? I had a brief look at the source, it's not exactly comment heavy but it's not unnavigable either - a higher level description would be cool. Also, I guess a little under half the ROM is the graphics - how big do you think a game like this could get with a simpler visual feel? (btw the graphics in Eaty are really good)
|
# ? Nov 4, 2015 17:40 |
|
Like I recommend in my documentation, I started this game by laying out graphics and planning a register map. I did some 128x64 mockup sketches, began slicing them and breaking out various pieces of animations. It's a little scary how quickly you chew through memory at first when you take this approach, but conservatively blocking out all the buffers and graphics you need early on makes it easy to prioritize features and get a sense of how close you are to the wire. I ended up with 153 bytes free, and I never had to sweat over carving out space to cram in something I felt was necessary. In Cave Explorer I made use of self-modifying code in several places to keep the text drawing routines as fast as possible. In retrospect, that was serious overkill. I didn't use any self-modifying code this time around. Eaty doesn't do any general-purpose text rendering, and the only time I have to hop back and forth between two values in i was during the process of horizontal scroll screen transitions where I have more than enough cycles to work with. I didn't write any custom data-preparation tools for this project, so I limited myself to data structures I could easily write and edit by hand. The world of Eaty is represented with two tables- rooms and loot-data. The former stores the graphical appearance of the overworld map- 64 bytes per room. The latter stores a list of item positions- roses, skittles, phone parts, etc- 8 bytes per room. With 5 rooms worth of data, that accounts for 360 bytes. The overworld representation is a sequence of pairs of bytes which represent an offset into the overworld tile data and a y coordinate. The superchip scroll-left and scroll-right instructions scroll 4 pixels at a time, so for silky-smooth transitions I split all the overworld graphics into 4x15 strips, wasting half of the pixels in each contiguous sprite. In retrospect, I think I could've just scrolled 8 pixels at a time and halved the storage of those sprites without a noticeable degradation of quality. By storing Y positions per strip, I was able to get skewed overworld sprites "for free". Here's what that inner drawing loop looks like: code:
code:
Logically the world is 1d- a looped linked list of rooms. Every 16-pixel wide strip of the overworld maps to one entry in the loot table, which makes displaying the item indicators very straightforward. The fifth room has fixed "loot"- the UFO pickup zone. At game initialization I then fill the lower 32 bytes of the loot table with random bytes 0-3. In pits, 1s are roses. Anywhere else (ie in an aboveground area) they'll be skittles. After placing inessentials, I do the quick-and-dirty approach for placing essential items- pick a number, check the table, make sure the location is suitable (parts go in pits, call points go on ground) and then place it: code:
code:
I think making a larger game world than this one is eminently possible, even without sacrificing too many graphics- coarser scrolling for the overworld could have halved both the size of my overworld tiles and the amount of data needed per room. Loot tables could have been byte-packed; I only used a handful of distinct values for each entry. There are probably a few hundred bytes to squeeze out by aliasing work buffers with initialization code, factoring reused code more aggressively and replacing a few "empty" sprites with special cases in code. The biggest challenge is always going to be representing more variety, so tricks like sprite skewing to get more mileage out of limited graphics data are helpful. I'd really like to see more attempts at roguelikes or adventure games on the chip8 platform- with cleverness and care you may be surprised how much you can pack into 3.5kb. Internet Janitor fucked around with this message at 02:13 on Nov 5, 2015 |
# ? Nov 5, 2015 02:05 |
|
Thanks for talking about Eaty, I'm really interested in the adventure/exploration genre and wonder how far it could be taken on chip-8. Additionally, it was suggested to me on IRC that I expound somewhat on pretty much the 2 main components of Octopeg: 1) dealing with large decimal numbers, in the order of hundreds of thousands or millions 2) Interesting looking collisions in an 8 bit math space. This is however going to come across as more of a badly written report on my experience writing Octopeg, so, bear with me. Honestly, skip to the start of the pictures if you want to know about the collisions, rather than the inane drivel that is me talking about my bad decimal math engine. Octopeg has a set of functions called BigDecimal for the score and the decimal mathematic trappings thereof. Naturally, I looked at the BCD function for this - it converts a 0-255 register into 3 bytes of hundreds, tens, and units, writing them into 3 bytes of memory based from I. The issues with this were that: a) I knew I was going to want to have a number bigger than this b) If you don't have any eg hundreds, you will still write the hundreds bytes value, so I can't easily pack them together in memory. c) The order in which the digits are written into memory - bcd writes in Hundreds, Tens, Units. This means tracking the I register backwards if your order increases to eg thousands etc. So I got to thinking and came up with a (not very good) solution: Using the same method you're taught as a kid to add up digits. Yeah... I decided to lay out, in memory, individual bytes for each decimal digit 0-9 for an arbitrary number of digits, in little endian byte order, so, the first byte is the units, then tens, then hundreds etc. I eventually had 3 of these memory addresses I could refer to, but in their infancy, they were just reading/writing directly to the score. These 3 memory area are A, B and C, running through each digit of A, add each digit of B, and put the result in C and carry any overflow above 10. And then all of the code would modify each of the acquisition functions to add one to the addresses, and repeat. If you've read that and thought about it any, I imagine you can see that it's actually kind of crap and could be done a lot better. My initial plan was to award points for every collision in real time, however, my strategy here ended up being far too slow, and I replaced it. However, this wasn't entirely a dumb solution. One of the key elements is that if you were adding a small number to a much larger number, you can stop early rather than process all digits. I decided that B would be the small increasing value (eg 150 points), terminated with 0xff, so in memory would appear as eg 0 5 1 -1. We can conclude two rules about this process: we know that we must continue until we hit this -1 in B, and until we have no carry value. So if we were to add this to 1239850, you would continue to carry into the 10000s column, observe that you have both run out of B to add, and your carry is now empty, and do not need to look at any further digits of the number, so you simply stop processing at the '4' in your result, 1240000. If no carrying were required, we would simply stop after we hit the -1 element of B. Not only does this save you from having to actually access the memory, it saves you from having to redraw, too. This, it turns out, is not the problem. This result worked ok for fixed bonus values, which I could prewrite into memory, and dealing with however many points you'd earnt during a turn, but, actually calculating those points in a turn was a different kettle of fish now. I decided that the scoring system for Octopeg should award you additional points the more full your combo/fever bar is, hoping to make it so trying to hit as many as possible with a full fever bar before dunking into a pit of your choosing was worth while. Tying these two things together wasn't straight forward, thus, I went back and reconsidered my score format. Drawing a much simpler 'you hit something' effect was top of the agenda, so, a memory value was read, incremented, and used for a single line's height - gradually filling the combo bar as you hit more pegs. The 'fill' amount of this bar was left in a register, and was the perfect tool for doing the scoring; I set a register to -100 at the start of each turn, and add to this the 'fill' amount, sans the 3 least significant bits. This meant that, for every 8 lines (lined up with the indicators on the bar), you would get an extra 8 points, so, 8, 16, 24 etc points per peg hit depending on the bar, plus an extra 20 if you had the bar filled (64 + 20) - the only requirement is that it be < 100. This value was added to my register storing -100. However, since -100 isn't really a number, really it's just 156, when you eventually add a total > than 100, it would result in an overflow and vf being set to 1. When vf is 1, you subtract 100 from the register, and I used a memory read/write to increase the number of 'hundreds or more I guess' in memory. Storing this overflow in a register would be important to speed things up. At the end of the turn, subtracting 156, or, adding 100, to the points tracker, combined with use of the previously discarded BCD command, would give access to the units and tens for your score that turn. Rearranging and copying it into my 'points this turn' memory address for BigDecimal, repeating for the 'hundreds or more I guess', followed by a -1, would offer the required format. I also did the classic 'put a fake 0 in' so you got 80 points+ instead of 8, so, the most points you can get in a turn is 255960 I guess - at the maximum bonus of a full fever bar, this would only be like 38 pegs, so, uh, that might actually be possible to hit but it's pretty unlikely. The short of it is, If I had to do it again (and I probably will), I would design it quite differently: Firstly, I would remember that you can do I += vn, a command I completely forgot existed until after I'd written BigDecimal. Instead of just having self modifying code that sets I to a given value and then applying a vn offset, it reloads the memory into v0 and v1, sets a working register to one, adds it to v1, handles overflows, and then resaves those memory locations, for all of A, B and C, for each digit, when it could simply store an offset value in a register and skipped 80% of that. Secondly, I would swap to a big endian byte order and simply start my offset at eg 7 and reduce it each digit so that BCD would be easier to integrate with it. Finally, BigDecimal was designed to minimise redraws, something it is no longer concerned with and was likely a misguided goal anyway. As a result, there's really no reason to keep it as single digits in each address - I'd pack each byte much the same way as I tracked points in a turn, and would BCD out the units and tens of each value when having to draw the score. Ultimately I'm not very proud of it at all, but, it did get me really comfortable making tools and dealing with self modifying code, so, there's that. What I am proud of, however, is the collision mathematics. I invested a lot of time into getting the collisions in Octopeg as reliable and as interesting to look at as I could. Collisions are of course, much more interesting with diagrams, so, there'll be some of those this time! The first step in having things collide is to know if they are colliding or not. Of course, we use chip 8's vf register to let us know if we drew over a pixel that was already on. However, what do we do next? I reuse the peg sprite, a single pixel, to search each pixel the ball is composed of. You may note the testing order is a little unusual, but, that's how I ended up doing it. When vf is triggered again during this 1 pixel search, you detect which pixel of the ball collided with the peg. This is just the first step in a long and arduous process, but I hoped to use the above image to draw you in~ Really though from here I need to talk about the ball, and how it moves. The ball's position is expressed to some extent by using 'subpixels', a technique where you define the position, and sort of more importantly, the velocity, with greater precision than you can actually display. There are two obvious ways you could do this: Firstly, in lores mode, the display is only 64 x 32. These only require 6 and 5 bits to define every pixel of. This means that you could store additional information in the 2 and 3 respectively unnecessary bits of the 8 bit registers you define the x and y position in - for example, 0 through 7 could all be quantised to a height of 0, 8 through 15 to 1, and so on. The main problem with this strategy in Chip-8 is that bit shifts are one bit at a time, so you'll need a lot of operations to redraw them, which is no good for a high speed ball moving game that will move every frame. The alternative is to have dedicated subpixel registers. These are registers that you add your velocity into instead of your position directly, and you modify the positions only when you eg under or overflow your subpixels. This is the strategy I used for Octopeg. With the addition to these subpixel registers, I would need: position-x, position-y, velocity-x, velocity-y, subpixel-x, and subpixel-y. However, I found that I also required more information. Sign-x and Sign-y - easy to access sign bits of the velocities. This was because I needed to know if I should test for an overflow or an underflow of the subpixel registers, and how to modify the position register in such an event. An important factor to note is that, vx and vy are still stored as negative numbers when travelling in the reverse direction, and additionally, due to the seperate sign bits, the ball has a full range of speeds from -256 to 255. I could have really tightened up my code for applying the velocity to the ball if only I'd thought a bit more, but, I needed cycles for the scoring and collision process anyway and this didn't seem to be a big deal and it fell by the way side. If I'd done it right originally, the updates would look a little bit like this: code:
We have pixel information, how about if we attached horizontal bounces to two of them, and vertical bounces to the other two? What would that look like? By the way, some of these ball movement gifs are a little, weird? With how the ball moves? I messed something up and redoing it would take 30 minutes+, there's a youtube link at the end where they all look nice. Well, honestly that isn't too bad. But, there are some problems: 1) It feels very... boxy? Bounces are all very similar, there is no understanding of the ball as a circle and the peg as a pin. 2) Sometimes, something like this happens: What is happening here? Is it something we want? If not, how do you detect and resolve it? Let's consider what we have in our tool box to help us with situation 1. By assigning horizontal and vertical bounces to each corner, we are pretending that each corner is one side of a a square, and that we are colliding with other squares. It's no wonder it looks very boxy! What could we do instead? Well, really we have 'corners' instead of 'sides' - maybe we should pretend to be a diamond? How do diamonds collide? OK that's great, but, what does the transform that we apply look like at that point of collision? The answer in this case, is, noting that our y axis increases in the downward direction here, a reflection on the line y = x. In this case, our x velocity becomes our y velocity, and our y velocity becomes our x velocity. We do not however flip them. However, this only applies to 2 of our diamond sides, the other two sides reflect on a different line, the line y = -x. Hopefully you can guess from the diagram that if we had collided with this corner, the result will be a vector travelling in a -ve direction on both x and y, likely the vector (-4, -1), which is, x = -y, y = -x. So, let's swap our vx and vy, negating as needed, swapping sign registers et al. What would this look like? Well, that's... better? It does a lot of good bouncing around in addition to going around in circles a lot. Modelling ourselves as a diamond is a little better than modelling ourselves as a square, but, we're supposed to be a ball, circles can behave in both of these ways. Can we do better? When we test our ball vs the peg, we test which of our 4 pixels the ball is touching the peg with. This really only gives us a small amount of information. Can we get more information about where the ball is or how it is behaving with relation to the peg? The answer to this question is, of course, yes - we have subpixel information. If we were to look at just the most significant bit of our subpixel registers, this would tell us which corner of a pixel our ball is in. If we combine this information with our collision pixel, we dramatically increase the amount of information we have available: Now instead of just 4 zones to consider where the peg has collided with us, we have 16 zones to consider. With this multitude of zones, we could consider ourselves a square in the zones along the top bottom and sides, and consider ourselves a diamond in the zones in the corners. Because we can move a whole pixel in a single frame, which is now larger than a single 'zone', we can have the peg penetrate inside our ball model, so, we have to consider the 4 'internal' zones too. I chose to simply model these as diamond type collisions also. So now, instead of being a square or a diamond, we're pretending to be uh, this weird octagon shape: Now, technically, there's a small problem with the way we pretend to be this shape - Firstly, we don't have sub pixel information for the peg. Secondly, we only actually know the peg even exists when it's already within our 2x2 body. So, sort of what we're doing here is wrong? If you consider the opposing peg in the top left corner vs the bottom right corner, the ball can only use the extra subpixel information to move itself down and to the right, which results in an imbalanced new perspective on the peg's location. Fortunately, we can't really do any better than this approximation anyway, and this will happen fast enough that no one will notice or really stop to think about what might be happening. Anyway, having decided to just roll with it, we need to use some brain logic. Our collision pixels are numbered from 0 to 3, and we can combine our sy and sx msbs into the 2nd and 1st bit of another value, to create a similar 0 to 3 value. Observe this figure I drew: So for example, if we are in the 00 pixel, then we want to be a corner in state 00 or 11 subpixel, and a side in 01 or 10. Corner for 01 in 01 or 10, side in 00 or 11, etc. You may quickly observe that the logic for this is: if CollisionPixel == SubPixel or CollisionPixel == (SubPixel ^ 0b11) then Corner else Side (^ in this case being XOR). This is a test we can easily do in CHIP-8, and it is in fact the exact test that I perform. You can talso perform tests on bit 2 of CollisionPixel vs Subpixel (match: top/bottom, different: side - eg, cp = 0b01 and sub = 0b11 => side) to work out if we want to present a horizontal or vertical side when we pretend to be a square. Now, what does THIS look like? Getting better. But, there's still some problems. We're modelling our ball a lot better now, but, a whiles back we noticed a problem. This: What's going on here? In this case, our ball happens to move on x and y such that it sort of... catches? the peg with its corner. Back when we recorded this footage, we were only doing a horizontal inversion, so, here's a diagram of what's going on here: How do we describe what is going wrong here? In just this case, if we were bouncing, ideally, we would be leaving the way we were already going. If you're mathematically minded at all, you could say something like the dot product of our relative entry velocity and a line perpendicular to our line of reflection vs our relative position vector & the same having the same signs means it will be bad. If you're not mathematically minded at all, then, well, this: So... given uh, that, we need to somehow detect and resolve this. For a horizontal bounce like this, this is straight forwards and we actually don't need to do any maths at all really: If our horizontal velocity sign matches the appropriate bit of the detected pixel as 0bYX, then, don't bounce because it will look weird? This kind of filtering works ok for our 'square' type collision, but, if this is a problem here, it's also a problem with our diamond type collisions. How do we test the complicated dot product thing I said earlier when we reflect on the line y = x? Well, this is actually remarkably simple. The relative position business is implicit to which of our pixels we collide with. We do however need to analyse our relative velocity. Or really just our velocity because the peg can't move. Let's look at this figure: Here I've drawn an 'X' over our ball, and drawn on several velocities it might have. It's colliding with a peg in its upper right corner, and it's modelling a corner/diamond type collision. We expect it to reflect on the line y = x. If we came in with the Green velocity, we would expect a result that looked like the Blue velocity. And I'm sure if it came in with the red velocity it would look fine too. But, if it collided while travelling with either the Blue or Black velocities, how would it bounce? Remember that it CAN collide like this, even though the peg would have sort of had to have phased through part of the ball - we don't get to model that event as our collision data will only trigger on whole pixel moves, all we have is the state we've found ourselves in. In these cases, it would bounce 'towards' the peg, which is the precise case we don't like. If we discount the impossible zone, where if we were travelling with a velocity in that zone, we could never collide with the peg unless we moved 2 pixels in a single step, then we can combine our sign information with another piece of information to detect these situations. If the X sign is Negative, we have a problem only if we are in the LEFT/RIGHT area of the X. If the Y sign is Positive, we have a problem if we're in the UP/DOWN area of the X. If we are not in the corresponding areas, then there is no problem. Additionally, since we know we can't be in the impossible zone, we actually know what kind of bounce we could do *instead* of a diamond bounce. If we were the blue line, we could do a vertical bounce, and if we were the black line, we could do a horizontal bounce. That's great! Now, how do we know which zone of the X we are in? Well, it's actually super simple: if our X velocity is greater than our Y velocity, then, we must be in the LR zone. If it's not, then we're in the UD zone. It's that simple. I wrote all of the pixel match ups vs LR/UD states by hand, I'm sure there's some bit comparison you could do, but given Chip-8's options, I'm not sure it would work out faster. Now that we filter these 'unwanted' collisions, what does this look like? That's not bad... but, can we do better? Currently, we present a very large 'flat' surface to our pegs. These result in horizontal or vertical reflections. These are boring reflections, and they don't add much dynamicism to our ball as it bounces around. Could we take our diamond type reflections further? What if instead of presenting as that octagonal shape from earlier, we present ourselves as this shape: These new edges would generate reflections on the lines y=2x and y=0.5x (and the inverses there of). Having solved our problems with reflecting into pegs by comparing the ratio of y vs x, we can imagine that we will need to do the same thing if we want these fancier reflections. Fortunately, we can do a couple of bitshifts, if we shift right, then we divide by 2, so, we can compare vx with a shifted vy, and a shifted vx with vy. If 0.5 of vx is still bigger than vy, then, we must be really moving it from left to right, hence, we're in a super LR zone, and if 0.5vy is bigger than vx, then we must be in a super UD zone. We can use these zones to filter out our unwanted reflections for these news lines of reflection, although we do need to select which one is appropriate for our collision type, so, there has to be some canoodling of data between our subpixel state and our X zone detection/selection. I opted to determine all the zone states into a single register as packed bits (so eg 0x7 would be in the UD zone for all Xs), and in a bitmask depending on subpixel state to determine which one we care about, and then tested if the X zone was 0 or not when filtering collisions. Anyway, so, while we can adequately filter these new collision types, how do we actually reflect on these lines. Well, I thought this was easy, 'oh yeah you just add some divided by two bits around it'll be simple'. I was quite wrong; the correct equation for reflecting on the line y = 2x is, as you can see here, x = 0.8y - 0.6x, y = 0.8x + 0.6y, or 4/5s and 3/5ths respectively. The different arrangements of -2x, 0.5x etc use these same coefficients, but, juggle the signs around, so eg 0.8 + 0.6 etc. Doing division by 5 was uh, not going to be a thing I wanted to do or even something I actually know where to get started with in 8 bit so, I decided that, maybe 0.75 and 0.5 would be ok. So that's 1 shift right for 0.5, and then another for 0.25, and then adding that in as required for each value. The first time I tried this however I ran into a problem. First of all, I completely lost track of the correct value for the separate sign bit, second of all, 255 * 0.75 + 255 * 0.5 is going to be > 255, so, there's an overflow problem to consider too. As a result, I decided that the correct course of action was to a) shift everything down by 1 and throw a bit away so that I could detect and cap the velocities when this overflow/underflow for -vs would occur and b) shift everything down by another one and use the most significant bit as a sign bit, and do real twos compliment math. This means that at the start of this routine, there's 3 bitshifts down, and then oring in 0xE0 if the sign bits are set to fill out the 3 MSBs correctly. The result is that, when shifting back up, you first shift out your new sign bit into vf, retain this in some way (I used a logic branch) then shift again and compare vf. If the first bit was 1 and the 2nd 0, then, we are travelling -ve and have had an underflow, thus, cap at -ve vel max, if the first bit was 0 and the 2nd was 0, then, we're +ve and we haven't had an overflow. In addition, there is the special case of 0b1100000, which will be -256 when shift back up. This isn't going to work for us unfortunately, so it has to be detected and capped to -ve vel max also. The ultimate result of all of this is actually to rob quite a lot of velocity away from the ball. As it turns out though, that's actually a good thing. Slowing the ball down in some way was an important elemnt, and in fact, it's probably one of the hardest elements to implement independently, and these bounce types gave it for free. And that's it, that's Octopeg's peg collision & reflection system in a very large and extremely longwinded nutshell. The final result looks like this: And here's that youtube link of all the bounce types with the right fps: https://www.youtube.com/watch?v=qY2OpMRxsGk edit - fixed a 0x11 to 0b11 and also VVV also technically I mostly meant that it would get a bad grade or something because I wrote 'I did x' a lot. Quaternion Cat fucked around with this message at 09:15 on Nov 6, 2015 |
# ? Nov 6, 2015 08:13 |
|
Mastigophoran your work on OctoPeg was great and your post made me appreciate what went into the game even more, thank you for writing your "badly written report".
|
# ? Nov 6, 2015 08:35 |
|
Fascinating devlog, Mastigophoran. Eaty The Alien takes a much simpler approach to score- I store life force in two bytes and cap each to a value of 99. I can then decode the value 2 digits at a time via bcd. All I needed for my mechanics was a routine for decrementing the life force (and checking if you died) and awarding extra life force. By making health pickups always come in multiples of 100 I could get away with simply adding to the high-order byte, with no need for carry logic. Collision is extremely dirty compared to OctoPeg- I used pixel overlapping as a cheap first pass and then if that was triggered I did an AABB-style overlap check. Eaty's fairly complex animations created a lot of problems, though, because pixel overlapping varied between frames- if you fall in a pit and then come back out in the pixel position you were at just before you overlapped with the pit you could easily end up in a different frame, and already be colliding. This inadvertently recreated the most annoying feature of the original E.T. game. Since I didn't have much time to shore this up properly, the quick-and-nasty fix to this was to detect these situations and then randomly teleport Eaty until he wasn't overlapping any objects. In the future I think it may be better to avoid trying to use animated sprites in combination with vF-collision at all. If anyone is thinking about doing this in their own games, go with a dedicated "collision mask" sprite which is drawn before the real sprite and which is consistent across animation cycles.
|
# ? Nov 6, 2015 16:07 |
|
Danm8ku postmortem / devlog I started off writing danm8ku as a bullet hell engine for chip-8 danm8ku games, and i built it upon this pretty simplistic game which shows off some of the features. I tried to keep the difficulty reasonable, but looking at the octojam twitch stream, I guess people without much bullet hell game experience just can't play these games very well anyways, so I might as well have made some patterns a bit harder. Writing this up with quite a lot of detail, but someone might find some stuff in here interesting. First off, here's what the game looks like when played through: == Title screen == The title screen is actually pretty neat. Here is everything but the title screen removed, running at a slower pace to show off how it works: http://johnearnest.github.io/Octo/index.html?gist=de467a09480be597df33 It first draws the text, and then increments a 8 bit binary counter for a random line. Basically, algorithm is (for one side of the screen):
Removing the text drawing part makes it even easier to follow (less than 50 bytes): http://johnearnest.github.io/Octo/index.html?gist=586e37d76decd37fc78f == Bullet engine == Most of the effort is put into the bullet engine, which is actually rather complex, since it requires allocating and moving up to 32 bullets on screen at the same time, which gets awkward even when running the game at 1000 cycles (Octo games generally have very few moving sprites). Each bullet takes up 8 bytes of space, containing:
In practice, very few bits of x acceleration are actually needed, and it should be possible to merge it with sprite offset, although it would require a bit more computations, and the engine is already running very close to the cpu limit, so this seemed fine. The bullets are stored in a 256byte dedicated area (memory address 3000), and should essentially be considered an array. Un-allocated bullets will have the x velocity set to 255, which is a magic value saying that its address is free for allocation. This should probably live in sprite offset instead, but whatever. Whenever a tick in the engine occurs, move_bullet will be done on each active bullet. This updates bullets positions, and sets a sprite offset based on the current direction. This method is heavily optimized in some non-obvious ways. One interesting difference is that x and y velocities are signed values, but they are not using ones compliment. Position is then updated based on velocity using the following logic code:
I tried doing something like this with "regular" signed arithmetic, but couldn't get it this fast. One nice thing here, is that v8 will be set to 1 if x velocity is positive, -1 if negative, and 0 if not moving. This means that we can do this for both X and Y, and then use these values to setup the sprite_offset byte to a unique value for all 9 possible movements: code:
code:
Data is: code:
Clear data works the same (see clr_nop and onwards). Most other stuff with the handling is pretty straight forward. Spawrning a bullet is done by doing something like: code:
code:
If the pattern should spawn lots of bullets (like pattern 2), the pattern will need to check that v8 is non-zero, after calling gogo_bullet, example: code:
Since the bullet engine has this nice interface described above for spawning bullets, the pattern engine can be pretty simple. Each pattern is just a memoryblock, ending with a jump to "pattern_done", these patterns are then store after eachother and accessed through a jump0 call. code:
code:
== Ending screen == The ending screen is also pretty neat, although less minimalistic (and not really optimized) compared to the startup screen: http://johnearnest.github.io/Octo/index.html?gist=d315690f033bde3188e4 Drawing it slowly should make it pretty visible that the algorithm for this is extremely simple: http://johnearnest.github.io/Octo/index.html?gist=20c8e5265cedf41265cc The data being drawn is the first 4 lines of the Zero in the built in Octo font. == Retrospective == Looking at the jonterp twitch stream of this game, where they were unable to get past the second level, I guess this game isn't really for everyone, but I only really wrote it for myself, and I'm pretty pleased with it. Some patterns are not very thought through, since I found the main engine the most exciting part, so I might come back to the engine later, and produce something new. We'll see.
|
# ? Nov 7, 2015 15:01 |
|
New game by a non-goon- Mastermind: http://johnearnest.github.io/Octo/index.html?gist=c4edfbcea14f960c628a William Donnelly, the fellow who submitted "SpaceJam!", intended to make this for the Octo-ber jam as well but ran out of time. Lots of details in the source code!
|
# ? Nov 9, 2015 00:28 |
|
Octo-ber Jam 2 playthrough footage is now up- Thanks to Jonterp and Everdraed for helping me record and edit the video, and thanks to Doug Crawford who provided some footage of games running on real COSMAC VIP hardware! https://www.youtube.com/watch?v=fxPoscaB8aE Edit: whoops, first upload was missing a few seconds of footage. New version will be up shortly. Internet Janitor fucked around with this message at 06:16 on Nov 9, 2015 |
# ? Nov 9, 2015 04:42 |
|
Those are some really good write ups. Thanks for taken the time to go into so much detail.
|
# ? Nov 10, 2015 00:26 |
|
Yeah I really enjoyed watching it, especially seeing the real hardware at the end!
|
# ? Nov 10, 2015 00:38 |
|
duomo fucked around with this message at 05:54 on May 4, 2016 |
# ? May 3, 2016 19:22 |
|
Is there a benchmark function for Octo to find out how many instructions/sec or /frame it can do on a certain PC?
|
# ? Jun 11, 2016 23:26 |
|
Not presently. Are you volunteering to write 2DMark '77?
|
# ? Jun 12, 2016 07:03 |
|
Internet Janitor posted:Not presently. Are you volunteering to write 2DMark '77? OK, sure. I was thinking of approaching it from the other direction -- that is, running a program and counting cycles in the emulator. But this was fun to make. I am not certain this is error free by any means, but it gets numbers close to what I calculate. I edited the octo cycles/frame dropdown and it works at 100000 but not 1000000. https://johnearnest.github.io/Octo/index.html?gist=08bbd489f8f6c5bc317a2f68a1a3b378 https://github.com/jdeeny/chipmark77 I suppose that .08 is a bug. taqueso fucked around with this message at 03:05 on Jun 13, 2016 |
# ? Jun 12, 2016 23:58 |
|
I tried this on a few devices, a samsung s2, an iPad mini 4 and running from 1 to 6 instances on my laptop, all report 60.08kOps - is this technique working as expected?
|
# ? Jun 13, 2016 00:25 |
|
Mastigophoran posted:I tried this on a few devices, a samsung s2, an iPad mini 4 and running from 1 to 6 instances on my laptop, all report 60.08kOps - is this technique working as expected? If the emulator is running at 1000 cycles/frame then it should report 60 kOPS. 1000 cycles/frame * 60 frames/sec = 60000 cycles/sec = 60k cycles/sec. It is only interesting if you can run the emulator without any cycle delay. taqueso fucked around with this message at 00:36 on Jun 13, 2016 |
# ? Jun 13, 2016 00:30 |
|
|
# ? Apr 25, 2024 13:52 |
|
I saw this thread was bumped so I had to make a program. Like all good programs, it's a modified version of the sample person program http://johnearnest.github.io/Octo/index.html?gist=6a3642baee4a53f8c6b2484eaa0fd3df WASD - move cursor V - draw C - erase 1 2 3 - choose brush Z X - choose layer
|
# ? Jun 13, 2016 03:09 |