Project Good Robot 3: Killer Robots

By Shamus
on Aug 21, 2013
Filed under:
Good Robot

92 comments

A quick reminder that while I’m writing this in the present tense (easier for me than switching tenses) we’re actually looking at this game as it existed three weeks ago. My first code check-in is dated July 29, so we’re visiting the past right now. If things go the way they usually do, then this series will eventually catch up to the present, since coding tends to slow down as a project grows.

So I’ve got my world of random walls and I’ve got a little avatar I can fly through it. I guess next we should work on AI. But to work on AI we need some foes. Well, there’s no sense in adding foes if they can’t attack me, and I don’t want to debug both AI AND shooting mechanics at the same time, so I should add shooting first. Of course, shooting will require collision detection. And to add these things to the scene I need textures for it all so I can tell one thing from another.

I’ll be using one texture for all the foes and projectiles in the game. In old 8-bit games this was called a sprite sheet and it looked like this:

gr3_sprites.jpg

Basically, you’ve got a big ol’ tablecloth of little images, and the program cuts it up and uses it according to whatever scheme the programmer devises. In a strict sense, I don’t think you can call what I’m doing a “sprite sheet”, although to my non-existent art team the two things would be identical. Sprite sheets are for sprites – which (as I understand the word) are usually raw pixel transfers of the source image. I’m not using sprites, I’m using polygons that kinda look sprite-ish. But I’m calling it my sprite sheet anyway. You can’t stop me. I’ve got the source code.

(For the rest of these screenshots, the player avatar is just a random image from my sprite sheet. None of this is how it’s supposed to look. While I was doing this coding I was also talking little breaks and making sprites to see what sorts of things look good and what doesn’t. I messed with colors, shapes, and I messed with background colors of varying levels of saturation and intensity. The screenshots in the next few entries will therefore seem kind of schizophrenic. There’s no sense in my trying to document the art changes, even if I remembered them.)

This sprite-sheet business is a huge timesaver. In a normal 3D game, you can’t just stick all your high-resolution wall textures onto a single texture like this. Well, you can, but unless you’re doing something fancy it’s not worth the hassle. You don’t want all the textures for your entire level stuck on ONE texture, since you probably don’t need ALL of them at any given moment. You’ll just be gobbling up GPU memory for no good reason and giving your artists a bunch of hassle. (I’ll bet arranging 1024×1024 textures on a huge grid would be a nightmare in photoshop.)

But here I don’t need a lot of pixels. This saves me the usual hassle of keeping track of what texture is in use, switching textures when needed, and designing the program to switch textures as rarely as possible.

Making sprites out of triangle pairs is effortless. Projectiles are easy in 2D. Collision detection is simple.

Next up is shooting.

In 3D, translating the mouse pointer into the scene and figuring out what is being clicked on is an exercise in madness. You’ve got to project the 2d position into the 3D space, which takes some math and can go wrong in a dozen different ways. Are you projecting a line from the center of the screen, looking for collisions for a hitscan weapon? Or are you translating the mouse position into 3D space to figure out what bit of scenery was clicked on? Do you need the actual position in 3D space or just the target object? (Or worse, the clicked position on the target object. Have fun with that.) Have you taken into account field of view? Aspect ratio?

Ha ha! We’re doing 2D, which means that converting from screen coordinates to world space is basically the same as looking at a map and converting centimeters to kilometers. You don’t even need to comment the code, because it’s so embarrassingly simple.

I set it up so that I can point my mouse somewhere on-screen and my little avatar will shoot at (towards) the cursor, letting me aim with the mouse. The only reason this takes more than five minutes is because I mistakenly believe it isn’t working. As I fly around the map I see my shots aren’t intersecting with the mouse target thing. It takes me a minute to realize of course the shots aren’t hitting the target, you’re dragging the target with you as you fly.

Oh. Duh.

Are we done with this foundational stuff? Can we write some AI now? Yes? Good.

(Two hours later.)

gr3_shooting3.jpg

Sidenote: Wow! JPEG compression does not like these shades of purple. Kinda turned my screenshots into mud. I didn’t really notice until I was done. I’m not going to go back and re-encode all these shots as PNG just so you can see these horrible graphics in crystal clarity. You get the idea. Let’s just move on.

Okay, I have a system where bad guys operate according to a few rules:

  1. If you’re too close to the player, back away. (While shooting.)
  2. If you’re far away from the player, close in. (While shooting.)
  3. If you’re at a happy distance, circle around the player. (Oh yes more shooting.)

Different robots have different pre-set “ideal” range, and they alternate which way they want to circle you. For each state they have a list of priorities. If they want to back away and they can’t (because you’ve got them against the wall) then they will circle. If they’re trying to circle and they can’t, they flip their circle-direction and try again. In short, these are some stupid robots. This is stone-age AI here.

And yet, it actually feels really good. Like, they’re dumb, but they feel devious. They blast you, but they’re hard to hit because they’re circling. So you close in to make it easier to hit them, but they back away. And you can’t head straight at them without running nose-first into their projectiles. And meanwhile the other robots are still pelting you.

These guys are really annoying in a good way. The way to beat them is to out-maneuver them, which is exactly the game feel I’m going for.

I was going to write A*, but I honestly don’t know what I’d use it for. I suppose I could use it to help a bot out if it gets stuck in a cul-de-sac. I don’t know if that will be a big problem or not.

gr3_shooting.jpg

This is a very strange game right now. I can’t die. It’s not even tracking damage. If I kill a robot another one instantly appears. There’s no sound system yet. So it’s this silent ballet where I’m endlessly attacked by an army of implacable flux capacitors.

I know it looks like MS Paint: The Movie: The Videogame Adaptation, but it’s actually turning out really well from a gameplay perspective. I’ve got bots that zap you from different distances and others that rush at you. (I could use these for melee or suicide bomb attacks.) You’re often engaging multiple foes at multiple distances and the only way to stay alive is to keep moving, keep dodging.

The only problem now is that with so many identical projectiles flying around it’s hard to tell which way everything is going. I’ll find myself plowing into slow-moving projectiles not because I ran out of room, but because I misjudged which way they were going. This is one of the things I dislike about shmups.

I enjoy zig-zagging through projectiles. When I get hit because I didn’t think fast enough, that’s fine. But in a shmup you sometimes get that feeling where the whole screen looks like noise. All the bullets look the same and when you get hit it’s not because you moved wrong, it’s because you didn’t even notice the bullet. Sometimes you just take damage and you don’t even know what you did wrong. I understand that’s how shmups work, but that’s not the kind of game I want to make.

I try elongating bullets so they have trails:

gr3_shooting2.jpg

That makes a huge difference. This visual change just made the game several times easier. I didn’t change bullet speed or projectile size. (Unless maybe bullets got slightly bigger.) But now you can judge the incoming angles and get a sense of who is shooting at you and where all the laser-bullets are going.

(That’s how lasers work, right? Gluons make the photons all sticky so they gather up on a big ball you can shoot at the enemy at speeds greater than a professional baseball pitch! These high-speed photons then unleash their energy on impact, which will either melt through the hull or burn through the hero’s shirt to expose his manly chest, depending on context. Science!)

I like where this is going so far. It’s ugly as hell, but it’s fun.

Enjoyed this post? Please share!

Footnotes:


2020202012There are now 92 comments. Almost a hundred!

From the Archives:

  1. rofltehcat says:

    I really hope this will get to a stage where we can play it somewhen :)

    Will you add Games For Windows Life so nobody can steal it?
    Just kidding ;)

  2. Kamica says:

    Don’t forget the ever present mist which accompanies lasers to give them that cool visibility!

  3. Atle says:

    Sprites were originally small separate hardware bitmaps that could be displayed independently of the main bitmap on the screen. This made it possible to have small moving objects without manipulating the content of the main image.

    Usually you had a hardware limit of like eight of these small buggers. But in the old days where TV was the monitor, you knew how fast the image was drawing and you could swap and move the sprites around when the image was drawn from top to bottom on the screen. When a sprite on top of the screen had been drawn, it could be moved to the bottom with different content, and be drawn again. This made it possible to have more than the hardware limited number of sprites on the screen at the same time.

    Good old times.

    • Septyn says:

      Amen brother! Coding for the 6502 and poking bytes between the HSYNC and VSYNC of the TV was a challenge but you could do some really neat stuff (for the time).

  4. Aegis says:

    I have to admit, I want to play this already.
    That sounds like a good sign to me.

  5. Strangeite says:

    This might sound odd but I’m really excited about the story and world that will accompany this game.

    • Aldowyn says:

      That does indeed sound odd.

      • Sabredance (MatthewH) says:

        Some royal ugly dude stole the Battleship Glory and you have to fight your way through 20 some-odd levels to get it back.

        At least, that’s what I’m feeling. Suddenly I’m nostalgic for the music…

        • Endominus says:

          You mean he stole the battle ship Glory, which is also the President’s daughter! And now the time streams are crossed, forcing you to fight velociraptors in spaceships to rescue her/it (pronouns are weird for people who are also pieces of military hardware).

          • Septyn says:

            But the problem is that the President is going to be late for his dance recital, and he’s spent too much time learning the watusi to let that happen! You need to rescue Glory so she/it can take dad/commander-in-chief to Dance Island, just to the east of Jurrasic Park.

          • Fleaman says:

            Space Battleship President’s Daughter

            The president’s daughter Glory is aboard the maiden voyage of the world’s greatest space battleship, which is also called “Glory” because the president is bad at naming things! Suddenly, the battleship is stolen by the Star Tsar, who plans to unleash its technology against his enemies, the Moon Nazis! But then also suddenly, a time machine blows up for some reason, throwing them all into a parallel universe! In this universe, the Lizardman conspiracy happened in Russia instead of the United States, so instead of secretly being evil alien lizards America’s leaders are actually super awesome transforming robots! Glory must now transform into the Super Space Battleship Also Named Glory to fight her/its way out of the clutches of the draconian Star Tsaur and his fleet of deadly Cosmoraptors!

            SEGA

  6. Aldowyn says:

    I like the glow effect on the first intro picture, that looks pretty sweet.

    It’s also pretty cool that you’re actually doing gameplay this time. Most (maybe all) of your other major programming projects were focused on very similar things – namely procedural terrain generation – and this is totally different and cool.

  7. Mephane says:

    Shamus, since you use the mouse for aiming, how does player movement work?

    – Movement relative to the player orientation. Inputs are “forward”, “backward” etc.
    – Movement relative to the screen. Inputs are “up”, “down” etc.

    Some games use the former, others the latter control scheme. Rarely does a game give the player a choice between the two (or freely mix them by setting up individual key bindings). Merely employing the scheme a player does not have good muscle-memory of can make a game considerable harder, and I have never made sense out of a game’s decision towards one or the other. It always seems completely arbitrary – maybe what the devs are more accustomed to?

    Obviously I prefer as much player choice as possible, but I am curious what your game does/did so far, and why you did choose so.

    • Shamus says:

      You move around using WASD for screen-oriented up / down / left /right movement.

      And not to skip ahead too much, but I’m trying to cobble together a control scheme that works with both KB+mouse and also a game controller. The game controller version is kinda “robotron”-ish. I’ll talk more about it when we get to that bit of the project.

      • Volfram says:

        Geometry Wars style. WASD+mouselook for KB+Mouse, left stick moves, right stick shoots for controller. If you would like a really good and fun example, take a look at Beat Hazard.(demo available on Steam)

      • TMTVL says:

        Being one of those weird European people with one of those weird “AZERTY” keyboards I wonder, am I gonna have to grab the source and edit it to change the key layout?

        • Bubble181 says:

          AZERTY unite! But that’s not European. Germans use QWERTZ, French and Dutch use QWERTY (but still different from the American version!). Heck, Belgium has two different keyboard lay-outs, both AZERTY but ith the accents in different places.
          I think there’re about 20 different keyboards in the EU alone.

      • Felblood says:

        So, just ape all the other smash TV knockoffs that have been produced since the introduction of the two-stick joystick.

      • Skuvnar says:

        I played a game a while back (I can’t remember what it was called) on Kongregate. The controls were mouse only and you moved you character by shooting. The negative force of the projectiles propelled you away from your firing direction.

        It was very strange to begin with, and then really interesting.

  8. Turbosloth says:

    I don’t comment often but I want to say that I really hope you turn this into a salable game. I would definitely buy it at a price range of ~5 dollars, and I know you and your family could really use the money (based on your “the 12 year mistake” series)

    Side note: I’d actually probably buy it 2 or 3 times, since I honestly think I would HATE it (it’s not my style of game) but I’d buy it for friends who play this kind of thing.

  9. broken_research says:

    Will you put this up on an indie marketplace to sell once it’s done?

    • keldoclock says:

      (That’s how indie games work, right? Steam Greenlight makes the gamers all sticky so they gather up on a big ball you can target at the marketing at speeds greater than a professional baseball pitch! These high-speed platforms then unleash their energy on impact, which will either melt through the wallet or burn through the hero’s credit card to expose his manly Kickstarter funding, depending on context. Marketing!)

  10. Daemian Lucifer says:

    “You can’t stop me. I’ve got the source code.”

    Screw the rules,I have the source code!

  11. Tim Keating says:

    > (I’ll bet arranging 1024×1024 textures on a huge grid would be a nightmare in photoshop.)

    It would, because that’s not what photoshop is FOR. Moreover, the big pain in the neck there would be extracting the texture coordinates when you’re done.

    Fortunately, we wouldn’t use photoshop because we have Texture Packer.

    • WJS says:

      Yeah, that was kinda my response to that bit. “Is he serious? I mean, I know he’s joking, but…” moment. (Fun fact: early minecraft used a big single texture for all it’s block faces. Texture packs are now made with individual files for each face, but I’m pretty sure it’s still all stitched together into a big megatexture thing internally. You just don’t have to to the stitching yourself anymore)

  12. Daemian Lucifer says:

    “(That’s how lasers work, right? Gluons make the photons all sticky so they gather up on a big ball you can shoot at the enemy at speeds greater than a professional baseball pitch! These high-speed photons then unleash their energy on impact, which will either melt through the hull or burn through the hero’s shirt to expose his manly chest, depending on context. Science!)”

    Ah,the science texts of xbone marketing team.Such incredible knowledge do they give us.

  13. HeroOfHyla says:

    Do your projectiles have velocity relative to the player ship, or do they come out at the same speed whether the player’s going forward or back?

  14. hborrgg says:

    This is sort of coming out of a discussion from the last post.

    Do you need a z axis if you are trying to make sort of a 2D/3D game like Atom Zombie Smasher, or is it easier to use some sort of layer/math shenanigans?

    • ET says:

      Judging from the gameplay video on their website, I would use an existing 3D engine for the 3D-on-a-2D-plane gameplay.
      The only time it would be easier to roll your own math, would be if it was something like a completely 2D game, but with a zillion parallax layers.

    • Volfram says:

      Ultimately that depends entirely on your specific game.

      I’m working on a 2D Metroidvania platformer(the original working title was literally “Metroid Clone”) where all the characters are sprites on a 3D background. Because all action takes place in a 2D plane, I’m doing collisions in 2D, so that needs no 3D shenanigans, but because the levels are all Minecraft-style blocks, the data for those of course need a Z parameter.

      That plus background and foreground elements, and multiple levels of abstraction… I think the answer to your question in my particular case is “both.”

  15. Volfram says:

    “It’s this silent ballet where I’m endlessly attacked by an army of implacable flux capacitors.”

    That could be a metaphor for life or programming or something.

    I added it to my quotes archive, regardless.(With proper accreditation, of course)

  16. Jabrwock says:

    I remember doing some testing for a WWII game, and they lined up tanks in a row, and we spent the afternoon hosing them down with small-calibre fire, to make sure the hit detection wasn’t letting .308 fire get through seams in 2″ armour. Every now and then we’d hear the programmers ask for a break while they studied the calculations on the one bullet that passed through 3 layers of armour to hit a fuel line on the far side of a tank.

    Bullets weren’t the biggest problem though. It was usually the big guns. The 88mm AT guns had a tendency to think a round had passed right through a tank, doing zero damage, and obliterating the target behind them. That’s the problem when your physics starts getting long range, to keep the calcs simple and fast the math starts getting flaky.

  17. SteveDJ says:

    So, with all those bullets flying around, I do hope that these killer robots will be subject to their own friendly fire? :-)

    …because I just KNOW you want to add AI so that the robots only shoot you, and not their buddies?

    • Hitchmeister says:

      If they are capable of friendly fire, that could lead to a level where you have no weapon and must maneuver in such a way to clear all the enemies by getting them to shoot each other. Either slowly re-spawning as long as any are left on the level until you get a simultaneous kill of the final pair, or possibly some environmental hazards that you could lead the last enemy into.

      • Viktor says:

        I’d make a significant portion of the enemies in that level suicide-bomber types. Head towards the player and detonate when close enough, with an X delay before the actual blast so the player can escape. If you place them right the player should be able to avoid unwinnable situations most of the time.

  18. Hal says:

    I’m assuming this game should have difficulty levels. What’s your preferred solution for adjusting the difficulty here? Changing the power of the player and the enemies (i.e. stronger player, weaker enemies?) Or changing the AI to make the enemies slower/less responsive/etc.? I’d assume the former to be easier, but everything I know about programming came from this blog.

  19. Dev Null says:

    Because I am that lazy coder who reuses things endlessly – and because I like the shapes – I think you should use your random geometric avatar generator to make shapes for the baddies.

  20. Anachronist says:

    “As I fly around the map I see my shots aren’t intersecting with the mouse target thing.”

    One game I saw, and I don’t remember what it was (might have even been on an Apple II), solved this problem by leaving a little target mark on the screen wherever you clicked your mouse. It eliminated the feeling of missing when the shot didn’t intersect the mouse, because you could see the aim-point left by the mouse click after you moved the mouse away.

  21. Ozy says:

    If you’re moving at a constant speed, the shots should still hit the cursor, since the projectile will have the speed of the player added to it when its fired, right?

    • Tomas says:

      Well, since they are lasers (light), their speed should be absolute…

      • Ozy says:

        If they are light, then their speed would appear to be the same in all inertial reference frames, so it would still hit the cursor.

        Though, if that’s the case, then the player is traveling at speeds at which relativistic concerns become significant and this would be a way more interesting project.

  22. Tomas says:

    “My first code check-in is dated July 29”

    Just curious, what version control system do you use?

  23. That last screen is kind of giving off a TRON vibe.

  24. anaphysik says:

    Shamus, I hate to shatter your conceptions of the world, but as a Professional LASER Expert I’m required by laser-law to inform you that that is NOT how lasers work.

    What you’re describing is actually the mechanism behind the (often-confused-for-a-laser) lazer.

  25. Squash says:

    “You don’t even need to comment the code, because it’s so embarrassingly simple.”

    Famous last words
    (I admit to being totally ignorant of programming)

    • ET says:

      Yeah, even for something this simple, I’d throw in a one-line comment, just to make sure I can remember how I combobulated the stuff for screen size/mouse location/window size/etc.

  26. Phantos says:

    I know it looks like MS Paint: The Movie: The Videogame Adaptation, but it’s actually turning out really well from a gameplay perspective.

    Speaking as someone who spends his free time making highly-detailed sprites in MS Paint, the stage musical was better.

  27. Kronopath says:

    I actually wrote some posts [1 2] on my little-frequented blog a few years ago on how, with just some basic physics and simple behaviour rules, you can create a set of AI behaviours for a top-down shooter that look very intelligent, even when they’re not. It’s always impressive to me when so much can be done with so little.

  28. Viktor says:

    “Ha ha! We’re doing 2D, which means that converting from screen coordinates to world space is basically the same as looking at a map and converting centimeters to kilometers. You don’t even need to comment the code, because it’s so embarrassingly simple.”

    I really hope this was foreshadowing for a later post on this same subject with “I screwed up WHY didn’t I comment this code?!”.

    • SteveDJ says:

      Now I’m thinking of that NASA probe that they tried to orbit around Mars back in the late 90s, but because someone confused pounds vs newtons, it crashed on the planet instead.

      I wonder if that could have been avoided by a simple comment in some code somewhere…???

      • Shivoa says:

        Bad type system (typedef int lbs; //hohoho, now you can’t add lbs and newtons without a cast that the most primitive checker can warn or even refuse to allow to the compiler without fixing) or identifier naming policy (int somethingInLbs; //hohoho, now you know what I put in here and what units I’m using) rather than comments. Check your project manager’s preferences in the style document, both seem to be workable and bring the information with the item so whenever referenced in an IDE the information is there (rather than a comment you may only see if you read the code around the declaration).

        I’d heard the story in relation to one of the Mars probes and km vs miles and didn’t realise it was a SI/English error that fell out in lbs vs newtons. It is apparently the expensive lesson that eventually got NASA to conform to the scientific community’s use of SI units for all their work. I’d also heard it was an API issue and so I’d say the simpler solution of being more verbose with identifiers (given in public interface names) was the obvious solution they failed to use at a cost of $125 million.

        • Shivoa says:

          Speaking of lessons from history (that now get taught in classes because the waterfall of mistakes looks more like a fabricated example than a real thing that happened and blew up $370 million of equipment). (Condensed version from memory:) The thing that didn’t need to be running at that point… which was just copied over from an older system with different requirements… and was a homogeneous redundancy with no variations to protect it… had an overflow issue that should have been caught… but, for performance reasons that looked more reasonable during the older project it was taken from, wasn’t… and triggered a debugging mode not needed in the final system… that effectively pushed junk data into a different system… and cause the failure that rotated the nozzles that forced the destruction of the spacecraft.

        • Santa says:

          Actually I believe at that point in time NASA had already a strict “SI-only” policy, which was being strictly enforced. NASA employees are expected to always use the metric system, even in casual conversation. The screw up was due to some third party company which supplied some programming or module of their own, and they used the incorrect measurements, clearly against NASA specification. NASA never caught the problem since they have explicitly eliminated having to worry about convention mistakes, and so no one ever doubted it. It probably still was a small miracle that the bug went undetected for so long, though.

        • WJS says:

          Do typedefs actually work like that? I’m sure I’ve seen that solution suggested before, and criticised because as far as the compiler is concerned, trying to add lbs and kgs is just trying to add int and int, which is completely fine.

  29. MaxEd says:

    This project of yours reminds me of Kris Asick’s Vectorzone a lot:
    http://www.pixelships.com/vzone/index.html

    Although he seems to be stuck in design hell with it.

  30. Bropocalypse says:

    Damn, I was looking forward to the A* posts, since I’m going to need to implement it or something similar for my own project.

    • Volfram says:

      A* is relatively simple provided you can use programming magic to “know” the absolute distance already.

      • Bropocalypse says:

        I’ve recently found JPS, and that’s pretty damn sexy, so I decided to go with that instead.

        • Volfram says:

          Oh WOW, that’s some good stuff!

          Documentation suggests it’s based on A*. Seems to find good solutions faster than reducing the accuracy variable in A*, though, so that’s good. I’ll have to check it out.

          To save future generations a little time, JPS stands for “Jump Point Search,” and Wikipedia DOES NOT currently have an article dedicated to it.

  31. JarvisTheJellyfish says:

    I assume so but I am just going to ask – are you using OpenGL for this project? considering that’s pretty much all I’ve seen you use i assumed so, but hey, never assume.

  32. allfreightoncanals says:

    I’m a particle physicist (and I’m currently at a particle physics conference, so that makes me, like, an expert squared) and I came here to say that your description of gluon-photon interaction is EXACTLY right.

    Well, there was a slight mistake. Obviously the correct reference speed is that of a cricket ball, not a baseball.

    I hope that the final game involves hero manly chest exposure! :)

  33. Michael Pohoreski says:

    Just a friendly FYI: The technical term for “sprite sheet” is “texture atlas”.

    Reference: http://en.wikipedia.org/wiki/Texture_atlas

  34. Neil Roy says:

    I actually use Photoshop to create sprite sheets. There’s a nice script I found that basically takes any number of layers and converts them into a sprite sheet. You just edit the script to tell it how many sprites per row you want and it does the rest. Load in each image, add them to a new layer, run t he script and save. I love it. It’s called “layersToSpirte.js” (you know, I never noticed the spelling error in that before, but that is how it is spelled).

    Oh, as for graphic quality, I am not much of an artist either, but that is what they invented 3D rendering software for. I use truSpace 6 (I paid for a few years back, now out of business, you can download truSpace 7 for free still, and there are many other packages for free and pay). Render my sprites to nicely lit 2d images, they look great, I can light them ahead of time. Rotate the object and spit out the images needed etc… then load them all up into photoshop, use that script and boom, I have a nicely rendered sprite sheet.

  35. deloise wininger says:

    Thought-provoking post – I was enlightened by the analysis . Does anyone know if my assistant might grab a blank CMS 1500 example to type on ?

Leave a Reply

Comments are moderated and may not be posted immediately. Required fields are marked *

*
*

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun.

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>