1. Trang chủ
  2. » Công Nghệ Thông Tin

Visual C# Game Programming for Teens phần 8 ppsx

47 210 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 47
Dung lượng 793,31 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Ranged Chance To-Hit = DEX + D20 Rolling for Damage If the to-hit roll results in a hit, the next step is to roll again to determine howmuch damage was done to the target.. Ranged “Damag

Trang 1

whereArmor Pointsrepresent the sum total of all armor items andShield Points

represent the defense value of an equipped shield I say possible way because this

is not the only way to perform the calculation Some game systems do not allow

a DEX bonus for plate armor wearers because that represents a slow-moving

character, whereas high DEX represents high agility To keep the rules simple in

Dungeon Crawler, I just apply the full DEX and full AP to the calculation

Based on the type of gear available in your game, you may want to add a

modifier to the AC calculation to help balance the gameplay a bit if it seems that

too many attack rolls are an instant hit I would expect about half of all attacks to

fail when rolled against a foe at the same level If you find that significantly more

than half of all attacks are succeeding, then that’s a sign you need to add a

modifier to the AC (such as +5)

Melee “Chance To-Hit” Rolls

The mechanics of combat for any game is entirely up to the designer The

important thing is not that your game works like many other RPGs out there,

Figure 12.13

Six different dice with 4, 6, 8, 10, 12, and 20 sides Image courtesy of Wikipedia.

Creating the Combat System 311

Trang 2

only that combat is balanced within your own game system In other words, aslong as the PC and hostile NPCs attack with the same set of rules, then the game

is playable One thing you really don’t want to happen is for combat to end tooquickly It’s generally necessary to artificially raise the hit points (HP) ofmonsters at the lower levels so they don’t fall with one hit You want the player

to feel as if real combat is taking place, not that they’re just walking aroundtaking out enemies with a single blow as if they’re using a lightsaber We dowant the player’s attributes to play an important role in the to-hit roll as well asthe damage done in an attack

For Dungeon Crawler, I’m going to use a D20 (a 20-sided die) as the basis forthe to-hit roll In RPG lingo, a D20 roll of 1 is an epic fail while a roll of 20 is acritical hit, which usually means a definite hit (ignoring the defender’s AC)

Melee Chance To-Hit = STR + D20

Ranged “Chance To-Hit” Rolls

Ranged attacks with a bow or spell are similar to melee with a D20 roll, but withDEX instead of STR as a modifier The character’s agility contributes to hisability to hit accurately at a distance, where his strength has little or no effect

Ranged Chance To-Hit = DEX + D20

Rolling for Damage

If the to-hit roll results in a hit, the next step is to roll again to determine howmuch damage was done to the target This is where the weapon attributes comeinto play If the game features real items that you can give your character to use

in combat, then it makes a big difference in the gameplay For one thing, you canscatter treasure chests around the game world that contain unique quest items(like magical swords, shields, and armor), as well as valuable jewels and gold.(These types of items are all modeled and available in the sprites provided in theReiner’s Tileset collection.)

Melee “Damage” Rolls

The melee damage value is calculated primarily from STR and weapon damagewith a 1D8 roll added to the mix This damage factor is then reduced by the

Trang 3

defender’s AC to come up with a total damage, which goes against the defender’s

HP

Melee Damage = D8 + STR + Weapon Damage - Defender’s AC

Some games apply a different die roll based on the type of weapon, such as a

2D6 for a two-handed sword, 2D8 for a two-handed mace, and 1D10 for a bow

You may use modifiers such as this if you want, but it adds an additional bit of

information to the item database I found it easier to use a base random die roll

(D8) and the weapon damage as an additional die roll The result is very nearly

the same, but it results in more reasonable weapon damage factors For instance,

we wouldn’t expect a rusty short sword to deal 12–16 damage where normally it

should be 1–4 By using the D8 roll in addition to the weapon damage range, the

damage factors will be more reasonable

Ranged “Damage” Rolls

The ranged damage value is calculated primarily from DEX and weapon damage

with a 1D8 roll added for some randomness A range penalty is then subtracted

from the total to arrive at a new attack value, which is further reduced by the

defender’s AC The final value is the total damage dealt against the defender’s

HP

Ranged Damage = D8 + DEX + weapon damage - range penalty - Defender’s AC

Ranged damage differs slightly from melee due to the range penalty, but it’s a

reasonable subtraction, because without it the player would be nearly invincible,

able to deal out full damage at long range where no monster would ever be able

to catch him before being cut down

Critical Hits ( “Crit”)

If the chance to-hit roll of the D20 results in a 20, then the attack is a critical hit

and incurs additional damage! You may add whatever modifier you want to the

attack damage factor, such as a 2x roll factor So, if the damage was calculated

with 1D8, then the critical damage will be 2D8 Optionally, you may just double

the 1D8 damage roll Remember, your system doesn’t have to mimic the combat

mechanic of any other system—be creative and unique!

Creating the Combat System 313

Trang 4

Attack Roll Example

Let’s simulate one half of an attack round where just one player attacks and theother defends, to see how the calculations are done and what results we get First

of all, we’ll give the player these attributes:

First, we’ll calculate the AC for the monster:

AC = DEX + Armor Points + Shield Points

AC = 14 + 12 + 0

AC = 26

Attack Roll

Now, we’ll calculate the attacker’s attack chance to-hit:

To-Hit = Attack Roll (STR + D20) - Defender’s AC

Attack roll = STR + D20

Attack roll = 18 + 9 (roll) = 27

Did the attack succeed?

Trang 5

To-Hit = Attack Roll (27) - AC (26) = 1 (Hit!)

Damage Roll

Since our attack succeeded, but was not a critical hit, we calculate normal

damage

Damage = D8 + STR + Weapon Damage - Defender’s AC

Damage = roll (1-8) + 18 + roll (2-8) - 26

Damage = roll (3) + 18 + roll (7) - 26

Damage = 3 + 18 + 7 - 26 = 2

Had the attack been a critical hit with an attack roll of 20, then critical damage

would be calculated with a factor of 2 as follows:

Damage = D8 * 2 + STR + Weapon Damage - Defender’s AC

Damage = roll (1-8) * 2 + 18 + roll (2-8) - 26

Damage = roll (3) * 2 + 18 + roll (7) - 26

As you can see from these results, the die rolls are crucial! After all those many

calculations, our hero only dealt 2 points of damage to the monster, and the

monster then gets to strike back at the player This continues round after round

until one or the other loses all their HP or flees

Dealing with the Player ’s Death

One drawback to combat is that you can die It’s a cold, hard, truth, I realize, but

it can happen What should you do, as the game’s designer and programmer,

when the player’s character (PC) dies? That is a tough decision that requires

some thought and should be based on the overall design of your game You

might let the player save and load the game, but that takes away from the

suspension of disbelief You want the player to be completely immersed in the

game and unaware of a file system, an operating system, or even of the

computer You want your players to be mesmerized by the content on the

Creating the Combat System 315

Trang 6

screen, and something as cheesy as a load/save feature takes away from that I’lladmit, though, most players abuse the save/load game feature and complain ifyou don’t have one After all, you want the player to be able to quit at amoment’s notice without going through any hassle Let’s face it: Sometimes thereal world asserts itself into the reverie you are experiencing in the game, andyou have to quit playing.

But just for the sake of gameplay, what is the best way to deal with the playercharacter’s death, aside from having a save/load feature? I recommend just re-spawning the PC at the starting point of a level file The location of a re-spawn is

up to you as the game’s designer Do you want to make it too easy for the player

to die and come back too quickly, or do you want to make them work a little bitbefore resuming the fight they were in previously? Re-spawning too close to thelast fight might make the game too easy, so a spawn point at a central hub town

or other location might be better, and then the player must walk and portal toget back to the location where they were at prior to dying

Combat Demo 2

The second Combat demo shows how to make these calculations for an attackagainst an NPC (Figure 12.14) This demo uses the Dialogue class to show theresults of attack rolls with each part of the calculation shown for you to study.This scene, for instance, shows a critical attack roll that dealt 14 damage to atarget NPC Most RPG purists will enjoy seeing this information, whereas casualRPG fans will prefer to just hurry up and kill the monster so they can loot itscorpse for items and gold It’s up to you to decide how much information youwant to share with the player

On the one hand, it might be impressive to see what all is involved in an attackwith the various rolls and calculations, since the casual player might just assumeyour combat system uses a simple attack roll versus defense roll system If youdon’t show any information, and just show damage dealt (as in games likeBaldur’s Gate), the player might assume just a random attack roll is all there is to

it Every attribute is important and affects the outcome of combat, and everyplayer knows this intuitively, but it’s easy to forget if combat tends to happenvery quickly One advantage to turn-based combat is that it will reflect a pencil-and-paper game, which is at the root of every computer RPG On the other

Trang 7

hand, some players might get annoyed with the slow pace of combat and give up

on your game You have to decide on the best balance between information

overload (TMI) and dumbed-down gameplay

Turn-based Combat

When a turn-based combat system is the way to go, we need to make a few

minor changes to the input system In the previous example, we used the Space

key to trigger a flag called attackFlag, which was set to false when the Space

key was released That works for a real-time combat system, but not for a

turn-based one For turn-turn-based combat, we need to wait until the user releases the

attack key Otherwise, some sort of timing mechanism must be used and that

can get messy So, here is the new keyboard code—note howattackFlag is now

handled

Figure 12.14

Demonstration of an attack roll against a hostile NPC.

Creating the Combat System 317

Trang 8

private void Form1_KeyDown(object sender, KeyEventArgs e)

{

switch (e.KeyCode) {

case Keys.Escape: gameover = true; break;

private void Form1_KeyUp(object sender, KeyEventArgs e)

{

switch (e.KeyCode) {

case Keys.D: keyState.right = false; break;

case Keys.Space: attackFlag = true; break;

} }

More Dialogue

We need theDialogueclass again to show the results of an attack You can nowsee how usefulDialogueis beyond its original intended use as a way to talk withNPCs! Granted, the window is not very attractive yet We will need to add somemore configuration options to it so the buttons look better and the height isadjusted automatically to the number of buttons in use But, the important thing

is, we have a way to interact with the player Before using it, we need to addsome new features to theDialogueclass See, I warned you that this was likely to

Trang 9

happen! But, we can’t possibly foresee in the future what new things we’ll need

to do with our code, so this is to be expected

As you’ll recall, the Dialogue class will display the dialogue window until a

button is clicked, and then set the Selection property equal to the button

number Previously, the Dialogue class did not hide itself after a selection was

made or reset any of its properties The new feature we need to add is aVisible

property

private bool p_visible;

public bool Visible

{

get { return p_visible; }

set { p_visible = value; }

}

TheDraw()function will checkp_visiblebefore drawing anything Now we will

have the ability to continually update the Dialogue object and have it display

whatever we want to the player, and selectively show it as needed

public void Draw()

{

if (!p_visible) return;

.

}

Back to our main source code for Combat demo 2 Here is the newdoUpdate()

function, which now handles scrolling, hero, monsters, attacking, and dialogue

private void doUpdate()

{

int frameRate = game.FrameRate();

int ticks = Environment.TickCount;

Trang 10

game.Print(0, 0, "Monsters in range: " + monstersInRange.ToString());

game.Print(320, 570, "Press SPACE to Attack");

game.Update();

Application.DoEvents();

} else Thread.Sleep(1);

}

ThedoDialogue() function does not automatically move, but you may use thatfeature if you want (see Chapter 15 for details) I want the combat dialogue tostay in the same place

private void doDialogue()

dialogue.Visible = false;

dialogue.Selection = 0;

} }

The doDialogue() function is called continuously from the main loop, andproperties determine what it should do To trigger a dialogue to “pop up,” wecan call on this new showDialogue() function, which automatically formats thedialogue with two buttons:

private void showDialogue(string title, string message,

string button1, string button2) {

Trang 11

ThedoAttack()function handles a single round of combat Well, technically, it’s

just one-half of a round since the NPC doesn’t fight back yet Study the

calculations in this function to learn more about how the armor class, attack

roll, and damage roll are related

private void doAttack()

{

const int DEF_ARMOR = 10;

const int DEF_SHIELD = 0;

const int WEAPON_DMG = 5;

bool hit = false;

bool critical = false;

bool fail = false;

AC = monsters[target].DEX + DEF_ARMOR + DEF_SHIELD;

//calculate chance to-hit for PC

Trang 12

{ //normal hit roll += hero.STR;

if (roll > AC) hit = true;

text += " + STR(" + hero.STR.ToString() + ") = " + roll.ToString() + "\n";

}

//did attack succeed?

if (hit) {

//calculate base damage damage = game.Random(1, 8);

//add critical

if (critical) damage *= 2;

text += "Damage roll: " + damage.ToString() + "\n";

//add STR damage += hero.STR;

text += " + STR(" + hero.STR.ToString() + ") = " + damage.ToString() + "\n";

//add weapon damage (usually a die roll) damage += WEAPON_DMG;

text += " + weapon(" + WEAPON_DMG.ToString() + ") = " + damage.ToString() + "\n";

//subtract AC damage -= AC;

text += " - monster AC(" + AC.ToString() + ") = " + damage.ToString() + "\n";

//minimal hit

if (damage < 1) damage = 1;

//show result text += "Attack succeeds for " + damage.ToString() +

" damage.";

Trang 13

else

text += "Attack failed.\n";

showDialogue("Attack", text, "Attack", "Cancel");

}

Facing Your Enemies

It goes without saying that attacking an enemy who is behind you is kind of silly

No, it’s ridiculous No one can swing a sword accurately behind them, let alone

shoot an arrow backward So, the game shouldn’t allow it either! What’s worse,

we can deal damage to a monster without even swinging at it The code that

figures out the direction to a target is like the code that sets the player’s

animation based on its direction The getTargetDirection() function will

“point” a character from its current angle toward a target This is also useful

for pitting NPCs against each other, or for having NPCs face the player when

you talk to them Figure 12.15 shows the Combat demo 3 running with new

code to cause sprites to face toward each other

H i n t

Note: the code in these examples is not meant to be based on step-by-step modifications to the

first example, but only to show the most relevant code as the chapter example is developed You

will want to open the complete project for each example and observe it running as you study the

text These projects do evolve toward a final, working combat system, but the complete code at

each step is not listed.

Which Way Did He Go?

The logic behind figuring out the direction from one point to another is really

just about brute-force If statements First, we look at the X position of both

points to find out whether the target is left, right, or directly in line with the

source Then, it checks the Y position to figure out whether the target is above,

below, or right in line with the source Based on these conditions, we set the

source in a direction that will most closely match the target’s location (within

the limits of the 8-way directions for our animations)

private int getTargetDirection(PointF source, PointF target)

{

Facing Your Enemies 323

Trang 14

int direction = 0;

if (source.X < target.X - 16) {

//facing eastward

if (source.Y < target.Y - 8) direction = 3; //south east else if (source.Y > target.Y + 8) direction = 1; //north east else

direction = 2; //east }

else if (source.X > target.X + 16) {

//facing westward

if (source.Y < target.Y - 8) direction = 5; //south west Figure 12.15

This demo shows how to cause sprites to face toward each other in order to fight.

Trang 15

else if (source.Y > target.Y + 8)

direction = 7; //north west else

direction = 6; //west }

direction = 0; //north }

return direction;

}

Using this function, we can modifydoMonsters() and force the PC and NPC to

face each other when the player triggers an attack! The result is much improved

over the previous example

//is player trying to attack this monster?

//make PC and NPC face each other

int dir = getTargetDirection(monsterCenter, hero.CenterPos);

Trang 16

} }

A Change of Character

A minor change is required in the Character class to support the feature offorcing sprites to face toward each other The original single Character.Draw()

function is replaced with these three versions:

public void Draw()

case AnimationStates.Walking:

p_walkSprite.Position = p_position;

if (p_direction > -1) {

startFrame = p_direction * p_walkColumns;

endFrame = startFrame + p_walkColumns - 1;

p_walkSprite.AnimationRate = 30;

p_walkSprite.Animate(startFrame, endFrame);

} p_walkSprite.Draw(x,y);

break;

case AnimationStates.Attacking:

p_attackSprite.Position = p_position;

if (p_direction > -1) {

startFrame = p_direction * p_attackColumns;

endFrame = startFrame + p_attackColumns - 1;

p_attackSprite.AnimationRate = 30;

p_attackSprite.Animate(startFrame, endFrame);

Trang 17

} p_attackSprite.Draw(x,y);

break;

case AnimationStates.Dying:

p_dieSprite.Position = p_position;

if (p_direction > -1) {

startFrame = p_direction * p_dieColumns;

endFrame = startFrame + p_dieColumns - 1;

p_dieSprite.AnimationRate = 30;

p_dieSprite.Animate(startFrame, endFrame);

} p_dieSprite.Draw(x,y);

break;

}

}

State-Based Combat

The combat system is now complex enough to require a state variable

Previously, a bool variable, attacking, kept track of just whether combat was

supposed to happen Now, we need to involve several steps for combat:

1 Player triggers an attack

2 Attack introduction

3 Attack commences

4 Report the attack results

This enumeration will handle the four states in the combat system:

public enum AttackStates

Trang 18

AttackStates attackState = AttackStates.ATTACK_NONE;

By the time we’re done adding state to the combat engine for this Combat demo

4 project, shown in Figure 12.16, the game will allow you to make distinct,individual attacks against an enemy with a click of the Attack button

Dialogue Improvements

Now that we’re using a state-based system for combat, we need to modify otherparts of the game code to also work correctly: namely, the Dialogue class.Previously, we just looked for a mouse click to trigger a button selection event.Now, that will not work because the dialogue will be repeatedly updated so amouse click will be seen as many clicks while the button is being held No matterhow fast you press and release the mouse button, it will pick up several eventsbecause the loop is running at 60 fps What we need to do is look for a buttonrelease event instead A new variable is needed:

Figure 12.16

The new state-based combat system slows down and improves the gameplay.

Trang 19

private MouseButtons p_oldMouseBtn;

When updating the mouse, we also need to keep track of the previous click state:

public void updateMouse(Point mousePos, MouseButtons mouseBtn)

And this change has been made to the Dialogue.Draw() function:

//clicked on this button?

if (p_mouseBtn == MouseButtons.None && p_oldMouseBtn == MouseButtons.Left)

p_selection = n;

else

p_selection = 0;

Plugging in Attack State

I will not go over every line of the next example, but suffice it to say there were a

lot of changes made to move the combat system over to a state-based system

The most important changes were made todoMonsters()anddoCombat(), which

mainly involved just checking the current state and acting appropriately For

instance, in doMonsters(), rather than simply setting the target to whatever

monster the player is close to without regard for any previously targeted

monster, the code now checks to see if the player isn’t already in a fight

//make PC and NPC face each other

int dir = getTargetDirection(monsterCenter, hero.CenterPos);

monsters[target].Direction = dir;

monsters[target].Draw();

State-Based Combat 329

Trang 20

dir = getTargetDirection(hero.CenterPos, monsterCenter);

hero.Direction = dir;

hero.Draw();

break;

} }

Also, after all of the monsters have been processed in the loop, then we need toreset combat if the player has walked away or cancelled combat The new

attackText variable is defined in Form1as a string

state condition

private void doAttack()

{

const int DEF_ARMOR = 10;

const int DEF_SHIELD = 0;

const int WEAPON_DMG = 5;

bool hit = false;

bool critical = false;

bool fail = false;

attackState = AttackStates.ATTACK_NONE;

return;

Trang 21

AC = monsters[target].DEX + DEF_ARMOR + DEF_SHIELD;

//calculate chance to-hit for PC

Trang 22

//normal hit roll += hero.STR;

if (roll > AC) hit = true;

text += " + STR(" + hero.STR.ToString() + ") = " + roll.ToString() + "\n";

}

//did attack succeed?

if (hit) {

//calculate base damage damage = game.Random(1, 8);

//add critical

if (critical) damage *= 2;

text += "Damage roll: " + damage.ToString() + "\n";

//add STR damage += hero.STR;

text += " + STR(" + hero.STR.ToString() + ") = " + damage.ToString() + "\n";

//add weapon damage (usually a die roll) damage += WEAPON_DMG;

text += " + weapon(" + WEAPON_DMG.ToString() +

") = " + damage.ToString() + "\n";

//subtract AC damage -= AC;

text += " - monster AC(" + AC.ToString() + ") = " + damage.ToString() + "\n";

//minimal hit

if (damage < 1) damage = 1;

//show result text += "Attack succeeds for " + damage.ToString() +

" damage.";

}

Trang 23

else text += "Attack failed.\n";

break;

}

}

Dealing Permanent Damage

The final step to complete the combat system is to give the player experience

after defeating a monster This will require some new fields in the Character

class since we did not account for experience or leveling when originally

designing the class Some additional code will be required in this final example,

Combat demo 5, to allow the animation for the killed monsters to stay on the

screen after they fall Figure 12.17 shows the result The amount of experience

awarded is a random value from 50 to 100, which is just an arbitrary range that I

made up as filler How should we award experience in the real game? It should

be a factor that involves the monster’s level, which is not something we’re

currently using in the character editor files What would you do with this design

decision: add a level field, or a pair of fields that define how much experience the

player receives? Something to ponder between now and the final chapter

T i p

Even this last Combat demo 5 project is not quite polished, but it is meant to serve as a working

example of combat For a production game, you would want the player to walk over the corpses

and stop them from “bobbing” as the scroller moves These issues are easily fixed as the final

version of the game demonstrates.

Dealing Permanent Damage 333

Ngày đăng: 14/08/2014, 01:20

TỪ KHÓA LIÊN QUAN