Thursday, May 21, 2009

Skirmish Update

  • Client actions working, single and multi-select.
  • Icons on units based on their class for now.
  • Zai rebuilt the main viewer; much nicer now. :)
  • Zai working on the selection information and viewer, Nick on the actions page.

Thursday, May 14, 2009

Skirmish update

  • Updated Movement system to handle the hexmap and non-intersection. Working on a priority system for uncontrolled motion (knockbacks and motion inhibitors).
  • Working on client; multiselect works, working on acting and invoking shared actions. Protocol for the client works, just need to make the data visible. Since we don't have the display engine yet, graphics are largely eschewed, using a very simple world viewer at the moment.
  • Tier 1 classes simple abilities are working and built automatically; still adjusting numbers for balance in the sim, but that's an ongoing task.
  • Writing Aux and NPC unit autobuilder for a PvE style simple scenario.

Formations, Motion, and dynamic combat.

A big question in my mind, regarding the development of the combat engine for Skirmish, is this: What makes this combat interesting and fun? Clearly this is an important consideration; if the main component of gameplay isn't fun, the game is pointless and forgotten. In addition to this we ask ourselves; How is it different from other RTS combat?

The answer isn't set in stone. We have some ideas, we're working on implementation. Iteration is the key; make something, then see if it's fun, and adjust or reinvent until you have something worth playing. One of our main concepts behind combat is making it dynamic, by keeping things moving and adapting to eachother. I'm going to focus on that aspect and try and communicate all of our ideas about it, rather than speaking broadly about the combat engine.

Formation is important. The player will be able to create arrangements of their units, physical distributions that control how they move and act. For example, a defensive oriented player formation might call for the primary units to huddle up and get close to each other. At least, that's what we'd think in historical combat. What we need to do is introduce concepts that make formations relevant, but without just declaring it so.

To clarify; we could easily make a 'Defensive Formation' that was huddled together and provided a defensive buff. This Is Boring. I don't like it. Making formations relevant without the cheeseball factor will yield more progressive behaviors, emergant behaviors that the player can exploit and learn about to become a better player.

So how does a defensive formation work? It reduces the ability to be attacked by multiple enemies, for one. This will happen by the nature of being adjacent to allies. Tight formations also mean that any attacker is in range of multiple defenders, making attacks harder to execute safely. How does this come into play, though?

How about we introduce an ability on certain classes that executes when a neighbor is attacked? An attack of opportunity, to use D&D jargon. Now say this ability isn't universal. Now you have a possible intelligent structure to your defensive formation; Counterers interspersed. Aha! Make your counter attackers longer range! Now you've evolved a phalanx, with no hackery involved.

What else? Well, let's make our defending units able to block neighboring attacks, a kind of intercept. Now the tight formation provides more defensive power, but ONLY when the formation is maintained! Which brings us to the next topic.

If formations are important, formations need to be broken. The defensive formation above needs a weakness, one that naturally evolves from its nature, rather than from numerical values. What's the disadvantage to clustering? Area of Effect comes to mind, if we include such style attacks. What else? Well, clustered units may have a hard time moving efficiently, so they'll have a movement speed issue. Does this happen naturally? Well, depends on how the motion system works. The movers are about half written, so we'll know soon.

But let's take a more direct approach. Let's introduce a mechanism to literally break up formations. Knockbacks. A new action that moves the target unit back. But! If the unit cannot move back, it deals additional damage, or perhaps a stun on nearby units as they get knocked over. Say your tight-formation group encounters a large creature, a giant or somesuch. He charges, smashes into your shield wall, and scatters your group. The group scrambles to recover into their formation, but each individual attack breaks the cohesiveness. The formation has a weak point! Excellent.

So we introduce local defensive effects, local offensive effects, knockbacks. But what incentive is there to 'not' cluster?

How about dodging? Let's change dodge from a simple chance to negate an attack, to a chance to literally dodge, gaining distance from the attacker. But the knockback caveat applies; you cannot dodge if you have nowhere to jump! So take a non-phalanx style group, say, a group of archers style stealth units. They need space to act accurately. Perhaps they gain a small bonus when they have noone near them, accounting for freedom of motion for managing a bow. This group is far more resilient to the giant, but for few numerical reasons. The giant cannot disrupt their attack nearly as easily as the phalanx, and each individual ranged unit maintains their offense, as the giant scrambles to smash each weakling into the ground, one at a time.

The last planned aspect of formations involes the roles of the constituents. Take a shieldman/archer style group, a few defenders and some ranged in the back. Assign the offenders a typical shoot-whatever-moves task, but task the defenders to intercept incoming units. They are there to guard, not attack. This involves a different style of AI and mover, one that can predict enemy motion and block it. The AI will want to focus on engaging foes, maybe stunning or slowing somehow.

There's an interesting word there: Engage. From my experience in simple combat, disengaging a foe you've entered melee with is risky. Again with the attacks of opportunity. Perhaps engaging a target in melee should codify that somehow. How bout we introduce a simple speed penalty when in melee range of a melee enemy. That is, an aura that effects enemy targets, slowing them? This lets a single guardian cover a small area, allowing the ranged units to attack with impunity. 

The combination of controlled and uncontrolled motion, formation changes on the fly, etc, etc, we hope will make a more interesting RTS style combat. Since the skirmish focus isn't on development as much as combat, we need the combat to be interesting. We need the units abilities to matter in a macro-scale kind of way. 

I guess we'll see when we get there.




Friday, May 8, 2009

Crazy GPU Tricks.

I haven't really posted yet on graphics programming. But my current task at Bunkspeed has me doing some crazy shader tricks, and I ran into something that GPUs do that is simultaneously awesome and awful, so I had to mention it.

If you don't know anything about GPU graphics reading this post will either confuse, fluster, or bore you. 

So here's the deal. You have a texture and some texture coordinates. Texturing correctly happens to involve calculating a differential equation of the texture coordinate over the screen. In the old style fixed function pipeline this is done for you. Well, relatively recently at least. If you remember how textures looked on the Playstation 1, how they kind of swam and made you sick, that's what happens if you don't do this differential correction.

Anyway, the hardware can do this for you since it knows what the texture coordinates are, since they're just in the polygons. But when we start using custom shaders that generate texture coordinates dynamically, this changes. If you can spit out any random number, how does the GPU determine this differential? It could make the shader generate it too, but it doesn't. The shader creator doesn't have to produce differentials himself, which is a very good thing; that'd be obnoxiously difficult.

To understand the answer you have to know something about shader execution. GPUs are fast because they are massively parallel. They execute many pixels in parallel. In fact, they cluster pixels together and run them in lockstep. That is, the shader executes for every pixel in the cluster at the identical time. Each pixel does every instruction simultaneously. 

So the processor cheats. To calculate the differential across the screen, it literally grabs the value from the next pixel over, subtracts, and calls it a day. It's perfectly accurate (if only first-order), and gets the job done, in most cases.

Which value does it compare to? Well, when you execute a texture lookup, it looks at which register the input texture coordinate is on, and uses that register index to lookup the neighboring pixels. Brilliant! So where's the problem?

Flow control. Say you have an if statement. Very simple. Not too common in shaders but we'd like them to be usable. Say you compute the texture coordinate inside the if statement. Say your neighboring pixel never went into that if statement. Now what? Your register has been computer, but the neighboring pixel has not! The differential is utterly invalid! Just because you used an if statement. Consider the following two blocks of shader code: 

float3 TC = float3(R.x, R.y, -R.z);
if (dot(R, R) > .01)
{
SecondaryColor.xyz += texCUBE(SpecularMap, TC).xyz;
}

if (dot(R, R) > .01)
{
float3 TC = float3(R.x, R.y, -R.z);
SecondaryColor.xyz += texCUBE(SpecularMap, TC).xyz;
}

Not functionally different. In fact, most C programmers would opt for the second one, because why calculate something outside the if when you never need it? BUT, the second one causes visual artifacts, while the first one does not! 

The second use of that differential calculation is to select which mip-level of the texture to use. If the differential is high, that implies the texture is being scaled down, so the GPU uses a small mip level. If you have a correctly built mip-chain, the worst you'll get is a small color abberation. But if you're using a rendered texture without a good mip-chain, well, you get garbage data. Here's my recent example for you, guess which is which.


The white edges on the cells are pixels where the differential is invalid, picking a white pixel out of the mip-chain. 

So yes, it's a wonderous hack, because it's capable of computing complicated differentials perfectly and automatically. But it's an awful hack, because it's extremely difficult to track down when something's going wonky, and because the fix looks like an arbitrary nonsensical change to the shader code.

But It Is Awesome.