Page top

2011/08/09 (Villainmad): A Not-Quite-So-New Beginning

Created:

Always use secure-HTTP / Secure HTTP / Permanent Link

First of all, we finally get to see Shintear's rendition of Villainmad himself! Thank you, Shintear, this is everything I could have hoped for! Except the shoes, but that's understood to be for another day.

Second of all, you know the drill by now. Here is build, here is source, XNA hasn't gone anywhere. The engine is now functionally at pretty much the same place it was when I started the Major Revision. A few remarks ...

  • You can now do "widescreen" fullscreen-resolutions from config.exe; I have not fully tested these, partly because one of them is bigger than my monitor's resolution allows. I may end up having to change how the config stores them altogether, for backwards-compatibility if I reorder them. (The "windowed" resolutions are most likely going to stay where they are regardless.)
  • Stage-levels and whatnot are compiled directly into Villainmad.exe, rather than being plaintext-compiled-at-runtime. Sorry, if anyone wanted to mess around without firing up Visual Studio.
  • Basis and GameState ended up keeping their names. Also, I am not using Nuclex's Game State system, just my own. It was just easier, really.
  • With a few ifs and buts, I fundamentally redid just about everything except the config. "Loading content" is a system that's used similarly by GameStates and Stages, instead of directly being a part of them, for instance. Menus and Stages inherit the "Command User" class, which (along with Game State) inherits the "Draw, Update and Take Input" class; Stages and GameStates (but not Menus) also inherit the "Content Load" interface, which requires that you have arrays of strings containing the filenames of the stuff to load. Shared data (stuff I want to be accessible and changeable within the GameEngine part of things but not the Game itself) is stored in an internal static class called DataStealer.
  • I have entirely done away with the system of EntityRefs and Used/UnusedIndices and whatnot, as it was almost entirely superfluous given what you can do with the List class. The system now deals with the GameEntity list directly. A side effect is that you can no longer change a GameEntity's "type"; I decided that there really weren't enough situations where this would be an actual problem. You now reference the GameEntity directly.
  • By the same token, each GameEntity type is now restricted to a single layer.
  • Game-makers now have read-only access to the entire list of Game Entities and Queued Commands
  • Added partial support for XACT, but I haven't really tested it yet.
  • I was unable to implement the "menu uses a screenshot of the previous screen as its background" deal; the most feasible way of doing so isn't actually possible if I want it to be compatible-in-general (i.e. with my Crappy Lappy) instead of just compatible-with-the-latest-and-greatest.
  • Pretty much everything else I mentioned in the Major Revision section last month (Except that I did put the entire engine into config.exe ...)

Some of the bugs I had were ... fun. And I wasn't able to see very many of them at the time, because I didn't have enough basic functionality to even start, until finally I was ready to implement the Stage class and augh nothing's working! The bugs included trying to use & to add to a bitflag-list instead of | (hint: & means "only things which are contained in both lists" instead of "all things which are in either list," and since they didn't have any in common, it emptied the list). Or the time I copied "check if the player is pressing up or down; if so, set the player's vertical movement; if not, set the vertical movement to 0" and pasted it so I could use the same setup for the horizontal movement ... and then forgot to change "set vertical movement to 0" to "set horizontal movement to 0."

My "favorite" bug, though, was when I had a List Of Indices Of Items To Be Removed From This Other List. The thing is, though, when you remove an element from a List, all subsequent indices change. When you want to remove 3, 5, and 9 from a list ... after you remove 3, the other two become 4 and 8, and 5 and 9 point to the old 6 and 10. Which made a mess. (My major code-malfunctions seem stem from not paying the precise amount of attention I should be. But I always figure things out EVENTUALLY!)

In other news, I'm thinking of migrating to another game-system. Apart From Anything Else, I've started to run into XNA's limitations for things like sounds; you can't really control them beyond "play, pause, stop" and "turn looping on/off" — you can't, say, specify what point in a sound-file you play at, or synchronize two music-tracks. (So no recreating Ten Desires, not that this was likely to come up in Villainmad, but for the long-term ...)

But there's also the lack of cross-platform compatibility, and I don't meen Microsoft's definition of "between Windows, Xbox 360, and Windows Phone." XNA's a bitch to get running on other systems, even by the usual standards of "put a Windows program on Mac/Linux." Then there's the fact that you just can't load fonts dynamically at runtime (or at least at load-time), which makes language-files a bit more ... touchy to deal with, so to speak.

And so I'm lightly poking at OpenTK or SDL.NET; I know that any system will have its limitations and learning-curves and whatnot, but some bits of XNA are getting to be a bit ... tough.

Todo-List

In no particular order:

  • Collision detection
  • Finish dealing with fullscreen-resolutions
  • Finish making the config-form.
  • Text/language services
  • Saving score
  • Replays
  • 3D backgrounds, although this is longterm on the "making multiple games" scale (as opposed to the "after I finish collision-detection" scale); Villainmad has always been going to be 2D, and games have been ruined before by unnecessarily doing 3D when 2D was perfectly fine.

So, yeah. We are, in fact, approaching "fully-functional game" territory.

25 Comments (auto-closed) (rss feed)

Spirit Tsunami

Snazzy suit. Kind of looks like something out of the Ace Attorney series.

Pawel

Snazzy suit. Kind of looks like something out of the Ace Attorney series. evil brother of Doc Scratch.
Fixed.

Kimiko Muffin

You saying Doc Scratch isn't evil already? :3c

GreatLimmick

His brother could still be evil.

a chicken passeth by

Well if you know what Sweep & Prune is, I don't think collision detection would take long...

Kimiko Muffin

I have that concept under control. The hard part about collision-detection is if I want to be able to have hitbox-shapes other than circles. I can probably handle ellipses, but I'm not too sure about rectangles, and let's not even start on collisions between different shapes.

But this is a solved problem and I can probably look around to see what others have done.

a chicken passeth by

Well the good news is, you probably don't need collision testing *that precise*. Well, as long as you aren't bouncing stuff off other stuff. Collision in any sort of 2D shooter usually results in the destruction of one object.

a chicken passeth by

Also, rectangle collisions should be trivial as long as they are not oriented BBs. If they are oriented however, Liang-Barsky is a textbook, thought in schools method that works on anything with 4 sides, and if that fails Cyrus Beck (you'd need to get a book for that...) works on anything without a concave edge.

Kimiko Muffin

/ Modified by Kimiko Muffin:

Well, I mean, if you have a rectangular bullet (c.f. Boss-Reimu's amulets), or a really long oblong laser (say, width of 10 and length of 90), you'd want it to have a hitbox which makes sense, especially given how precise you want your movements to be. I mean I can fudge things for the purposes of Villainmad, which isn't going to have shrine maidens throwing amulets, but if other people want to use the engine down the line ... (Disclaimer: I don't actually know how ZUN handles the hitboxen of amulets. Or lasers, for that matter ...)

EDIT: But for checking which "bucket" an entity falls into, yeah, it's going to be non-oriented rectangles. Easy with circles, not so sure about ellipses ...

a chicken passeth by

I haven't wrapped my head around oval-anything at the moment, but it occurs to me that if you're talking in terms of Rectangular and Oblongs that are Oriented, they're still 4-sides. Liang Barsky works here as long as you remember that it's a line clipping algorithm, and yes, it does work on oriented objects.

If the player's hitbox is squarish (4-sides), no problem - you need to test Liang Barsky both ways and see if any particular side of your rectangle clips into the other. This algorithm returns time units to collision per surface, so it's actually quite exact.

If the player box is a circle, you shouldn't actually be using Rectangle/Rectangle algorithms. The good news is, you can use Line-Circle algorithms vs all sides, which is far simpler than oval-anything or shape/shape. This also applies to Circle-Poly I think.

If you're doing Poly-Poly with >4 sides apiece, and none of the polys have circular collision boxes, you'll need to do a more complicated version of Liang Barsky, Cyrus Beck. Yes, it still works on oriented bounding boxes, but it has a particular restriction - your shape must NOT have a concave surface, so no "pits" (In fact, the standard operating procedure for dealing with concaves is to split the shape into 2 convex shapes and test against both. If you have concave shapes in your top down shooter, however, you'd better have a *very good reason*, because it's regarded as needless complication.) I don't think you need to go that far, tho, unless your bullets bend like the Pagoda's, but I think *that* is a combination of rectangles...

I used this to help me get my head around Liang Barsky.
http://www.skytopia.com/project/articles/compsci/clipping.html

a chicken passeth by

PS: if you need to do grazing hitboxes, I suggest you layer 2 hitboxes and then test against both. You'd only need to do this for the player, so it's one additional entity to test against. When you S&P the player, always use the *outer* hitbox.

Kimiko Muffin

I'm not planning on doing anything other than circles, ellipses, rectangles, and rotated rectangles (which will be two different things to make the former easier).

To calculate circle-to-ellipse or ellipse-to-ellipse collision, I'm planning on using this formula to get the "radius," and then just doing a distance-comparison like with circle-to-circle comparisons. (It also does "if (a == b) return a" at the beginning of the "distance" calculator, in order to treat it like a circle if necessary.) I was actually planning on doing a sweep-and-prune deal where it treated everything as a circle, with its radius as the furthest point on it (so, for rectangles, the distance from the center to the corner, and for ellipses, the semimajor axis), in addition to also dividing the playable-area into buckets (someone mentioned this in an earlier post, I think).

I'll look over that link to see whether I can use it or whether it makes me decide to drop rotating-rectangles altogether, thanks. And yeah, I know I need to do multiple hitboxes for grazing ... I'm going to make the detect-if-colliding methods accessible to the Game section, and you'll be able to have "custom collision-detection" thence.

a chicken passeth by

Well dropping rotating rectangles/squares altogether helps you eliminate the types of shapes you must consider, saving you the need to store the points of your oriented bounding box, and reducing all your collision tests to simple circle/circle. BUT remember that ellipses CAN be oriented (unlike circles), so don't think the rotation monster is going to spare you...

StackOverflow has this ongoing discussion that could help if you want to swing that way. http://stackoverflow.com/questions/2945337/how-to-detect-if-an-ellipse-intersectscollides-with-a-circle

S&P basically uses a maxima and a minima for any given collision box. You'll realize that even if you use x+-radius and/or y+-radius to calculate this, you'd end up with a Non-Oriented Bounding Box anyway. Not that there's a better way to do S&P; you'd STILL have to recalcuate these bounds every frame because the thing has moved.

Kimiko Muffin

Well, that Javascript bit in Wikipedia already takes the orientation into account. It's designed to draw a series of points around an ellipse, and bases this on a list of angles around the circumference. I suppose if nothing else, since I'm just trying to get the distance instead of an exact point, and we're already basing it on the angle of approach, I could always subtract the angle of orientation from the angle of approach.

Kimiko Muffin

Oh, look, an already-solved-by-someone-else problem.

Now, to apply this idea to ellipses .........................

Wymar

Is it weird that I just spent over five minutes doing nothing else than staring at the bullets and listening to the tune?

a chicken passeth by

SAT is just an extension of Circle/Circle collision for non circular bodies.

The good news is, it's the same concept as Non-Oriented Bounding Box collision. The only difference is that instead of using your x and y axis, you instead project both shapes onto ALL surface normals on ALL edges of both sides, and see if any of them don't have an axis.

The bad news is that you absolutely need at least one of your shapes to be a polygon, and while I think I know a way to make it work with circles, I have no idea how to deal with ellipses short of giving your ellipse a collision box. Keyword being "Project Onto Surface Normal", which circular shapes have infinite amounts of - you're better off using circle/circle or circle/ellipse due to the computation required.

http://www.metanetsoftware.com/technique.html, check out the first link. Every realtime collision textbook starts with SAT. You should also consider appropriating or buying Christer Ericson's Realtime Collision Detection.

Also, note that SAT does NOT return time to collision as it does not care if the objects are moving. It simply returns True or False.

a chicken passeth by

Accursed link is broken due to the comma....
http://www.metanetsoftware.com/technique/

Kimiko Muffin

And now it's broken due to pointing to a directory instead of an HTML! :D Here, have a link on how to make links.

That said ... Actually, first, I should probably, y'know, test the ellipse collision-detection code I made.

PriffyViole

Is it wrong that the first thing I thought of when I saw that picture was "Dear Villainmad, how do you type with a boxing glove for a head?"

Wymar

Like Strong Bad, except he bangs his head against the keyboard instead :P

Kimiko Muffin

Okay, first of all, I need to fix a bug — the "intersection" method doesn't properly take the angle into account, it just behaves as if it were at 0-rotation! And it isn't clear why it it is behaving like this, because the "Get the distance to the edge at the given angle" method just asks for the Length of the result of the "get the Vector2 point at the edge at the given angle" method, which works fine because it's being drawn just fine! GRAWR.

Second of all ... I'm not really concerned about anything other than "are they colliding?" Anything else is ... kinda superfluous.

And ...

The bad news is that you absolutely need at least one of your shapes to be a polygon, and while I think I know a way to make it work with circles, I have no idea how to deal with ellipses short of giving your ellipse a collision box.

Okay, this is the part where I become tempted to completely and utterly give up on anything except circles.

I need more sleep. :<

Kimiko Muffin

Okay ... I figured out what was wrong when I added a little visual-cue to the test-program: it's not actually using a functionally-rotated ellipse. It is using a circle, which is then squished and "visually" rotated. The "squishing" part means that, as you can see here and here, the white dots are supposed to be on a direct line between the centers of the two of them, but angles which are not multiples of 90 degrees get skewed away from the semiminor axis towards the semimajor axis (regardless of which is on which of the global X and Y axes); the "visually-rotated" part means that, demonstrated here, it is the ellipse's entire frame of reference which is being rotated — its very definition of X and Y and "zero degrees of rotation", as it were — not the ellipse itself mathematically (in retrospect, a pretty big giveaway was that before I added the white dots, all the other dots were actually revolving around the central point, not merely shifting their positions to accomodate the shape of the ellipse).

So ... no ellipses. Whee. Um, *thud*

a chicken passeth by

Sometimes I'm lazy to construct HTML forum tags. This should finally work. =.=
http://www.metanetsoftware.com/technique.html

Reason why I wanted you to look at that page is that it contains a pretty good explanation on SAT.

Personally I think it's easier to regard everything as a 4-sided. You can then use SAT or Liang Barsky and it'll all make complete sense. This will require you to store additional information such as collision boxes and surface normals, but here I agree with someone from the GameEntity post when he says "get it working first, optimize later".

Alternatively, you can regard everything as a collection of circles - including lasers - and this will make collision detection trivial to implement. And it'll still make complete sense. There are consequences to doing so, but if Touhou 13 is of any indication, I think Zun actually solved the dillema this way. >_>

Kimiko Muffin

Yeah, I think I'm just going to do "array of circles." I wasn't sure how I was going to detect what the other hitbox was without either using reflection or redundant data. (Mad Science is happening! I'm getting IDEAS now!) And the "it has collided" method will also include an array of the indices of the hitboxen it is colliding with.

Pondering how to deal with S & P in that case ... the easiest thing to do is, well, uh, the easiest thing to do would be to not bother. Second-easiest is to make it overridable and let the game-maker sort it out manually. Option 3: going through every single hitbox on the entity, and deriving one big circular hitbox from the biggest and/or furthest from the center.

Hm. No reason not to do 2 and 3 ... but yeah, I'll save S & P for after I've implemented basic-collision.