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

Building XNA 2.0 Games- P10 pptx

30 295 0
Tài liệu đã được kiểm tra trùng lặp

Đ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

Tiêu đề Building XNA 2.0 Games- P10 pptx
Trường học University of Example
Chuyên ngành Game Development
Thể loại lecture notes
Năm xuất bản 2023
Thành phố Sample City
Định dạng
Số trang 30
Dung lượng 391,04 KB

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

Nội dung

public class AI { public const int JOB_IDLE = 0; public const int JOB_MELEE_CHASE = 1; public const int JOB_SHOOT_CHASE = 2; public const int JOB_AVOID = 3; protected int job = JOB_

Trang 1

If you don’t want to flip back but need a really quick refresher, it goes like this: commands are declared and run in Script and parsed in ScriptLine

First, let’s declare our new commands in our enumeration:

Trang 2

We’re calling it AI for artificial intelligence, but make no mistake—there will be absolutely

nothing intelligent about our AI class We’re basically going to define a list of simple behaviors

(chase and attack, evade, stand still, and so on) in a base AI class, and then create

monster-specific classes that will decide which behaviors to use and when

Trang 3

Making artificial intelligence that looks and feels real is what is important It doesn’t matter how we do it, as long as the player believes that the zombies act like real zombies As an inde-pendent game developer, you should start to realize that the quick and hackish way is often enough, and that you do not need a strong core set of AI algorithms just to make a small game.We’ll call the current behavior a “job,” holding the value in the job field for a duration of jobFrame We’ll keep track of who we’re chasing or fleeing with the targ field—this will allow us

to have friendly nonplayable characters (NPCs) in an all-out side-scrolling zombie war, should

it come down to it

public class AI

{

public const int JOB_IDLE = 0;

public const int JOB_MELEE_CHASE = 1;

public const int JOB_SHOOT_CHASE = 2;

public const int JOB_AVOID = 3;

protected int job = JOB_IDLE;

protected int targ = -1;

protected float jobFrame = 0f;

protected Character me;

In our Update() function, we’ll take the array of characters, ID of the character we’re controlling, and map (it will be nice to know our surroundings, but we won’t be implementing that just yet) We start off by setting all of our character’s keys to false, and then decrement our jobFrame and call DoJob() to well do our job

public virtual void Update(Character[] c, int Id, Map map)

In DoJob(), we do some case-by-case behavior

protected void DoJob(Character[] c, int Id)

Trang 4

For all sorts of chasing and avoiding, we make sure we have a valid (greater than –1) target

If we don’t, we call FindTarg() and get a new one We also use ChaseTarg() and FaceTarg(),

which return false if they’re still working at getting our character within range and facing the

Trang 6

{

d = newD; closest = i; }

}

}

}

}

return closest; }

private bool FriendInWay(Character[] c, int Id, CharDir face) {

for (int i = 0; i < c.Length; i++) {

if (i != Id && c[i] != null) {

if (me.Team == c[i].Team) {

if (me.Location.Y > c[i].Location.Y - 100f && me.Location.Y < c[i].Location.Y + 10f) {

if (face == CharDir.Right) {

if (c[i].Location.X > me.Location.X && c[i].Location.X < me.Location.X + 70f) return true; }

else {

if (c[i].Location.X < me.Location.X && c[i].Location.X > me.Location.X - 70f) return true; }

}

}

}

}

return false; }

ChaseTarg(), AvoidTarg(), and FaceTarg() all return true if the character is in the wrong

position, meaning the character is still attempting to chase, avoid, or face its target When we

call these methods, we end up doing what we need to be doing when in the correct position

(typically attacking) if everything returns false We thought this way was intuitive, but if you

would prefer to word it differently, go for it!

Trang 7

protected bool ChaseTarg(Character[] c, float distance) {

if (me.Location.X > c[targ].Location.X + distance)

Trang 8

That does it for our AI class for now We can easily add new behaviors as we create new and

more complex monsters—for instance, a boss character that throws axes when its prey is at a

The zombie will chase our character, avoid our character, or stand still This is not exactly

groundbreaking behavior, but then again, it’s just a zombie

Dealing Damage

We need to add some functionality to HitManager.CheckHit() We now have ethereal

charac-ters that we can’t hit, as well as dying characcharac-ters that we can’t hit either In our big series of

conditions for checking hit collisions, let’s add another if clause to test for both:

for (int i = 0; i < c.Length; i++)

{

if (i != p.Owner)

Trang 10

At the end, if our animation had been set to hit, we set it to diehit if the character should

be dead If our character doesn’t have a diehit animation, we’ll just end up using the regular

hit animation

On that note, we also need to employ our dieland animation We set our enemy animation

to hitland in the Character.Land() method Let’s have it set to dieland if the character should

When we want to add some character building and depth, we could add a few lines in

KillMe() to create coins, health, and so on, as necessary For now, we can just leave it at setting

DyingFrame to 0, which signals that our character is dead

Lastly, let’s kill off our characters from Game1.Update() After updating our characters,

we’ll check their dying status—if dyingFrame is greater than 1, we kill them

character[i].Update(map, pManager, character);

if (character[i].dyingFrame > 1f)

{

character[i] = null;

}

That should do it We’ve created some blood-related triggers; created zombie death

animations; created and implemented new script commands; and added health, death, and AI

to characters Let’s run it We can kill our zombies now! Our zombie head splatter is shown in

Figure 9-5

Trang 11

Figure 9-5. Zombie head splatter

Map scripting presents a similar situation, but the question lies in balance between the map format and editor and the script functionality For instance, say we wanted to let some maps have fog We could handle that in two ways:

• Add a Fog check box to our editor In saving the map, we would write the fog value: true

On the other hand, the drawback of incorporating new map functionality into the map format

is that it requires interface changes (possibly major) So, if the file format isn’t planned for it (as ours isn’t), we would need to load and save every last map using the new format if we make a big change mid development

Because this section is titled “Map Scripting,” we’re going to choose the script ality road Let’s get coding! We’ll start by making a script editor in MapEditor

Trang 12

function-Adding a Script Editor in the Map Editor

Let’s declare some new fields at the class level of MapEditor First, we’ll declare a new draw type

in our DrawingMode enumeration Right now, we can draw in select mode, collision map mode,

or ledge mode Let’s add script draw mode We’ll also add a text editing mode for script editing

and some fields to specify the scroll value of our script and whether any script lines are selected,

as well as some highlighting colors for some simple syntax highlighting

int scriptScroll;

int selScript = -1;

const int COLOR_NONE = 0;

const int COLOR_YELLOW = 1;

const int COLOR_GREEN = 2;

In Draw(), we add some functionality to draw our script when we’re in script draw mode

if (text.DrawClickText(5, 25, "draw: " + layerName,

mosX, mosY, mouseClick))

spriteBatch.Draw(nullTex, new Rectangle(400, 20, 400, 565),

new Color(new Vector4(0f, 0f, 0f, 62f)));

spriteBatch.End();

Next, we’ll iterate through all visible script lines, drawing and handling selection

for (int i = scriptScroll; i < scriptScroll + 28; i++)

Trang 13

if (map.Scripts[i].Length > 0)

{

String[] split = map.Scripts[i].Split(' ');

The GetCommandColor() method will compare our commands against some strings we’ll give it

Trang 14

bool mouseDown = (Mouse.GetState().LeftButton == ButtonState.Pressed);

if (DrawButton(770, 20, 1, mosX, mosY, mouseDown) &&

Implementing Map Script Commands

Let’s use the GetCommandColor() method to talk about some of the script commands we’ll be

Map script commands are about the same as character script commands The only

func-tional difference will be that we’ll allow ourselves more than one parameter per command—a

few things would be impossible otherwise Here’s a brief explanation of what we’ve got so far:

Trang 15

fog: Turns map fog on.

monster type x y name: Creates a monster of type type at location x, y with the name name

A character name is a string identifier that will set a map flag when the character dies A flag

is a list of strings that the map system uses to keep track of things For instance, we can spawn two zombies named z1 and z2, and then end up in a loop that checks whether flags z1 and z2 have been set Flags exist on different scopes Local flags are reset every time a map is loaded Global flags are reset every time the user starts a new level, but are persistent other-wise (a player can move from one map screen to another and global flags will not be reset)

makebucket size: Creates a bucket of size size A bucket is basically a list of monsters that

will “empty” itself into the game as long as the screen monster population is less than size

We can test to see if a bucket is empty

addbucket type x y: Adds a monster of type type to the bucket The monster will spawn at

location x, y.

ifnotbucketgoto tag: If the bucket is not empty, goes to tag Tags are like goto labels We

always start with tag init

wait ticks: Pauses the script for ticks ticks.

setflag flag: Sets the local map flag flag.

iftruegoto flag tag: If local flag flag is set, goes to tag tag

iffalsegoto flag tag: If local flag flag is not set, goes to tag tag.

setglobalflag flag: Sets the global map flag flag As noted, unlike local flags, global flags

are persistent throughout the whole level A good application of this would be to use old (local) map flags to keep track of who you’ve killed within a room, setting a global roomcleared flag once all baddies are cleared Then when the player reenters the room, we’ll see the roomcleared global flag and won’t try to make them clear the room again

plain-ifglobaltruegoto flag tag: If global flag flag is set, goes to tag tag

ifglobalfalsegoto flag tag: If global flag flag is not set, goes to tag tag.

stop: Stops reading the script

tag tag: Sets a goto destination.

A simple script could look like this:

Trang 16

It should be easy enough to decipher what’s going on in this script based on the command

definitions and the basic intention of this game The script starts at the init tag, turns on fog,

goes to the last line, and stops if the roomclear global flag has been set Otherwise, it creates two

monsters, loops until they are dead, creates a monster bucket full of zombies, loops until they

are dead, then sets the roomclear global flag and stops If the player kills everything in the room,

leaves, and comes back, there won’t be any zombies spawning

Notice the wait statements When we set up the script-running method in ZombieSmashers,

we’ll have it keep running the script in a while loop until it hits a wait or a stop We can put in

a failsafe (to cry foul, for instance, if we’ve read more than 1000 lines of script in one go), but we

may as well get into the practice of putting wait statements within loops now

Updating the MapEditor Code

To better punch in map coordinates, let’s add some functionality to Update() that lets us just

click in the map to add an x, y coordinate to the selected script line:

((int)((float)mosX + scroll.X / 2f)).ToString() + " " +

((int)((float)mosY + scroll.Y / 2f)).ToString());

}

}

Trang 17

The next section we’ll update is PressKey() Our text-editing capacity is fairly rudimentary, but we’re going to make it slightly less rudimentary with the introduction of multiline editing When the user hits Enter, we need to increment the selected script line and push all lines below that down one When the user tries to press Backspace on an empty line, we decrement the selected line and pull all lines below that up.

private void PressKey(Keys key)

bool delLine = false;

Trang 18

If delLine is true, we’ll decrement the selected script line Otherwise, we’ll rewrite the

temp string back to our editing string

Our ScriptEnter() and ScriptDelLine() methods are as follows:

private bool ScriptEnter()

Trang 19

We need to modify the Map class to include the script array, as well as to load and save the new format At the class level, we’ll declare the script as follows:

public String[] Scripts = new String[128];

At the end of our Write() method, add this:

for (int i = 0; i < Scripts.Length; i++)

file.Write(Scripts[i]);

At the end of Read(), add this:

for (int i = 0; i < Scripts.Length; i++)

Scripts[i] = file.ReadString();

The tricky bit is dealing with already saved maps At this point in development, we have only one map, map.zdx To convert the map to the new format, we change only the Write() method, load the map, save it with the new format, and then change the Read() method If we had changed both methods, we would get an error while loading the map

Figure 9-6 shows our script editor in action

Figure 9-6. Script editor

Trang 20

In Figure 9-6, we’re using the following script:

Now we can set up our script processor and reader in the game

Implementing Map Scripting in the Game

Now we can add some classes to our game to process and run the script We’ll use a similar

system to what we used with the character scripting: a MapScriptLine class to process and

contain the script lines, and a MapScript class to manage and run the script We will also use a

new enumeration for our commands

Trang 21

public MapScriptLine[] Lines;

All of our map script command constants follow We’ll be using these when we process our map script line strings in MapScriptLine We’ll keep track of which line of script we’re currently reading with curLine

int curLine;

float waiting;

public bool IsReading;

public MapFlags Flags;

Our constructor will take a reference from the map that is using it, create a new MapFlags object of size 32, and create an array of 128 script lines The MapFlags constructor we’ll create will just take the size of the string array to create

public MapScript(Map _map)

{

map = _map;

Flags = new MapFlags(32);

Lines = new MapScriptLine[128];

}

DoScript() is the big method we use to run any and all script commands If the script is waiting, it will count down the waiting value a bit; otherwise, it will continuously run line after line of script until it’s told to stop

public void DoScript(Character[] c)

Trang 22

When a MapScriptLine parses a command, it will split the command into a string array

called sParam Also, for certain types of commands, it will convert what we intend to be vectors

(that is, x y coordinates) into a Vector2 called vParam, and integers into iParam

For our monster command, we use vParam for the spawn location, sParam[1] for the monster

type, and sParam[4] for the monster name

Trang 23

case MapCommands.SetGlobalFlag:

map.GlobalFlags.SetFlag

(Lines[curLine].SParam[1]);

break;

Ngày đăng: 01/07/2014, 22:20

TỪ KHÓA LIÊN QUAN