practical 2 jim finnis (jcf1@aber.ac.uk)users.aber.ac.uk/jcf1/cs12020_cgvg_prac2.pdf · making it...

Post on 30-Jun-2020

0 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

CS12020 for CGVG

Practical 2

Jim Finnis (jcf1@aber.ac.uk)

This week

● Solution to last week and discussion● Global variables and the model● The Main Loop pattern● States and the State Machine pattern● Random numbers● “Debounced” user input

The previous worksheet

● A common solution:

Go left using for.. loop

Go right using for.. loop

loop()

A good first solution, but...

● Difficult to modify● Each inner loop has to have its own copy of

some of the code– Clear-draw-swap

– In more complex programs this gets really nasty

● When adding new stuff it's easy to forget to add some of that framework code.

Don't repeat yourself

● If you write code that does something, don't just copy and paste it to do that thing again.

● Make the thing into a function.● If you want to do a slightly different thing, make

that function take arguments to change its behaviour.

● This makes your code much easier to modify and extend.

Use variables

● Instead of writing code that just does one thing (move left eight steps, for example),

● write one piece of code that does one thing several different ways using variables.

● Be lazy – don't write it five times, each slightly different. Write it just once, a bit more cleverly.

The Simplest Thing

● But don't be too clever – always think “What's the simplest thing that can possibly work?”

● And one piece of code is always simpler than five.

void setup(){    AberLED.begin();}

void loop(){    int x;    for(int x=0;x<8;x++){        AberLED.clear();        AberLED.set(x,4,RED);        AberLED.swap();        delay(300);    }    for(int x=7;x>=0;x­­){        AberLED.clear();        AberLED.set(x,4,RED);        AberLED.swap();        delay(300);    }}

void setup(){    AberLED.begin();}

int x=0;int dx=1;

void loop(){    AberLED.clear();    AberLED.set(x,4,RED);    AberLED.swap();        x = x+dx;    if(x==0 || x==7){        dx = ­dx;    }    delay(300);}

Global variables

● Are bad.● Unless you're storing data which is genuinely

global – which the whole program needs access to

● Such as the “game state” - the data about the game's “world” - also called the model.

● Object-oriented languages like C++ and Java have helpful ways of dealing with global data.

The Model

● Your game models (makes an abstract “picture” of) a virtual world – this is true of all games.

● This world might be very complex, with hundreds of entities made up of moving parts, as in a modern AAA title

● Or it might be very simple, like noughts and crosses.

● But there's always a model – the game's representation of the world as data.

The Model

● First-person shooters– The environment, consisting of 3D shapes, some of

which may be free to move

– Enemies and other creatures, which have a position and some internal state (pose data, weaponry, ammo, alert status, velocity)

– Objects in the environment, such as ammo packs, quest items etc.

– You and equipment you're carrying/wielding

Simpler models

● Chess– An 8x8 grid

– A set of pieces

● Noughts and crosses (Tic-Tac-Toe)– A 3x3 grid

– Each part of which can be marked X, O or empty

Step 1 in game development:define the model

● What is your environment (the unchanging parts of the world, the “game board”)?

● What are your entities (things in the world)?● How should you represent them as data?

– Variables, arrays, structures...

An example

● Chess is a fairly simple game, model-wise● How would you represent the state of the

game inside the computer?

Possible models

● 8x8 array of characters, each encoding a piece (e.g. “p” for pawn and “q” for queen, using upper and lower case for black and white)

● Two arrays of “piece” structures, one for each player, each containing– Whether the piece has been taken

– X,Y position if not taken

– The index of the piece in the array determines its type, e.g. whitePieces[0] is the white king, and so on.

Which is best?

● It's a good idea to design your model so that invalid states are impossible to represent. This really helps prevent bugs.– The second example is more complex, but does this quite well.

● On the other hand, it should be easy to perform the operations on the model which the game requires – moving pieces, etc.

● On the third hand, it should be easy to display the model to the user – to render it.

● For those who know chess: what is the problem with the second model?

● It's all about compromises.

Making it move: the Main Loop

● Pretty much every game has one of these, even if it's hidden inside the engine

● It's an example of a design pattern – a particular way of solving a problem, more general than an algorithm.– You'll see more design patterns in your second year.

● This pattern is very specific to real-time graphics (games etc.)

● (many engines hide the main loop from you)

The Main Loop

Read input and update model accordingly

Update model for time step

Render model

e.g. player presses FIRE, so create new missile heading in the

player's direction

e.g. for all entities, add the velocity to the position, and

do collision checks

e.g.● Clear buffer● Draw environment● Scan through entities

and draw them● Draw HUD● Flip buffers

Main Loop: handling user input

● Read switches and other inputs.● Process these inputs by modifying the model.● Must not block – that is, it must not wait for

anything (e.g. user input). It runs as fast as it can.– delays are blocking. We'll talk about these later.

– As an aside, all console games will fail product testing if the screen freezes for more than a fraction of a second.

Main loop: updating

● Change the game model (i.e. the game data variables) in other ways than user input changes.

● Typically these are changes due to time or game AI.

● Examples:– Moving objects (player, enemy, bullets..) through the

world by adding their velocity to their position

– See if any of the enemies have noticed the player and change their behaviour if so

Main loop: rendering

● Drawing things to the screen representing the model.

● Doesn't change the model

Keep everything separate

● The update and user input code shouldn't do any rendering

● The rendering code shouldn't do any updating● This helps keep your code easy to maintain

– You know where the code which draws stuff will be

– You know where the code which changes stuff will be

● An example framework for a Main Loop application.

● You need to add:

– Model data

– Set-up code

– Input, update and rendering code

void setup(){    AberLED.begin();    ... // set up initial state}

void handleInput(){    ...}

void update(){    ...}

void render(){    AberLED.clear();    ...    AberLED.swap();}

// do not modify below here

void loop(){    handleInput();    update();    render();}

Timers

● You shouldn't ever use delay() - why not?● The millis() function will return the number of

milliseconds since the program started.● You can use this to find the number of

milliseconds since something happened.

Timers

● Here's some code that will make something happens 2 seconds after something else:

as a global (part of your model):

unsigned long eventTime=0L;

starting the timer:

eventTime = millis()+2000;

somewhere else:

if((0L != eventTime) && (millis() > eventTime)){eventTime=0L; // reset the timer… do something

}

States

● Your game will probably have several different phases:– menu, gameplay, winning/losing screen, high score

table, etc...

● In each of these, render(), update() and handleInput() need to do something completely different.

● The code for this can get very messy.

State Machines

● We use the idea of a state machine to deal with this.

● Each different phase of the program is called a state

● The state machine can be in one of states, and can move to other states.

State Machines

● Have a variable (usually an int) with the current state

● Code in the main loop can then look at the state variable (using if or switch) to decide what to do

● The main loop update can change the state under well-defined conditions

● Like Main Loop, it's another design pattern

Shows only the game

Shows only the menu

State Diagrams

MENU

FADE UP

FADEDOWN

GAMESTARTShow both menu and

game

On “OK” pressed On fade up complete

On game exitOn fade down

complete

Show the states, and how states move to other states

State machine code

● A #define for each state● Global state variable initially invalid (-1)● gotoState(int s) goes to state s (by setting the state

variable to s), and stores the current time● GetStateTime() returns the current time minus the time

of the last call to gotoState()● You add switches and conditional code to update() etc.

which uses the state variable to decide what to do● This code is provided on the worksheet.

Work through an example

Random Numbers

● Are very important in games!● random(max) returns a random number from 0

to max-1● So random(10) returns 0,1,2,3,4,5,6,7,8 or 9

– Note that you get numbers up to max-1, not max!

– What would you use to simulate a six-sided dice?

Random Numbers

● Performing an event sometimes, but not always:

● random(10) will return 0 one time in ten● So if(0==random(10)) { … } will run the code in

the brackets one time in ten● This is useful for doing things at random

intervals in your update● You'll find this handy for the assignment

A Bit More User Input

● Finding out when a button has been pressed● You need to compare the previous state of the

button with the current state● But buttons are bouncy!

Debouncing

Button on

Button off

We want to ignore all these

Button has been down for long enough for it to count

getButtonDown(int n)

● Luckily I've written code which does this for you!

● Call getButtonDown(int b) to find out whether a button has gone down since the last interrupt, and has been down for at least 20 consecutive interrupts.

The assignment

● You should now know everything you need to do the assignment

● Design your model● Structure the main loop as described: handleInput(),

update() and render() functions all called from loop()● Use a state machine and switch statements inside

your main loop functions● Don't use a delay – use getStateTime() or millis()

(see this week's worksheet)

top related