Game Development Using C#
Game Development Using C#
Presents
Session One:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Introduction:
In partnership with Microsoft, DigiPen Institute of Technology is pleased to present a series of eight one-hour webcasts to introduce participants on how video games are produced. The main game development concepts will be demonstrated via the creation of a top-down shooter using C# 2005 Express Edition, Microsofts new lightweight integrated development environment designed for beginning programmers. Participants are able to download C# 2005 Express Edition along with webcast tutorial materials to experience building the game themselves. It should be noted that the intent of this webcast series is to provide an overview of the game development process and is not designed to train participants in all aspects of C#. Webcast Sessions: Session 1: Overview of Game Development Process Session 2: Basic Programming Concepts and Introduction to C# Session 3: Overview of Game Elements Session 4: Introduction to Sprites and Animation Session 5: Transformation and Collision of Sprites Session 6: Player Control of Sprites Session 7: Game Music and Sound Effects Session 8: Creating Sprite Behavior The DigiPen Game Development Webcast Series continues through May 2005. Go to http://www.microsoft.com/events/series/msdnvideodev.mspx to register or to access archives.
DigiPen Game Development C# Webcast Series - Session One, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
If we divide the second into 60 pieces Each piece would be 1/60 or 0.016 a second (this is also 16.66 milliseconds) If during each 16.66 ms we update sequentially all game component The game components would be updated 60 times a second The player will get the illusion that the events are coincident or parallel this means that the events are happening at the same time. o In reality the events are not parallel; they are pseudo-parallel o The events do not happen at the same time, they happen sequentially o Because the events are updated at a fixed interval of 60 times a second, the illusion of concurrent events is achieved o Each 16.66 ms duration is one game iteration o Since the iteration repeats as long as the game is playing, the concurrent events are controlled by repeating the game iteration through a game loop o The game loop also makes the series of events update as a motion picture or pictures in motion Game loop o The game loop iteration duration greatly affects the illusion of concurrent events o If the duration of the game iteration is long, lets say 0.1 s, then the simulation will feel slow o Why? Because the reaction to the events happens only 10 times in one second o Show the game at 10 fps, 30 fps, and 60fps o On the other hand if the duration of the game iteration is short, like 0.016 s, then the simulation feel smooth o Why? Because the reaction to the events happens 60 times in one second o Consequently, the duration of the game iteration is called the frame o Therefore, the game speed is measured by frames per second o For example, when we say a game speed is 60 frames per second or 60 f/s or 60 fps, then the game iteration duration is 16.66 ms How do we add interaction at real time with concurrent events? o During the game iteration, we: Detect and register the user input Execute the behavior of each object; usually the object behavior depends on: Input from the keyboard Input from other objects Collision status AI Etc. Once all the objects are updated, their position and status at the current game loop is determined We render the objects What we just mentioned means that the objects are rendered as many times a second as the game speed For example in a 60 fps game, the objects are rendered 60 times a second During the beginning of the rendering during the game loop, a blank frame is prepared Then all the objects are rendered sequentially When an object o moves, its position at game loop n is slightly different than its previous position at game loop n-1; also, its position at game loop n+1 would be different than the position at game loop n Show example of the moving bullet at a low frame rate
DigiPen Game Development C# Webcast Series - Session One, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Frame n
Frame n +1
Frame n +2
DigiPen Game Development C# Webcast Series - Session One, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Being drawn several times consecutively, the sequence of different pictures and different positions provide the illusion of a motion picture As a matter of fact, movies at the movie theater play at 25 pictures per second Game components o Background: static object o Sprites: dynamic objects o Text o Sound Sound effects Music o Object behavior: specifies its interaction. Overview of the game flow
Game Loop
Initialize game Initialize starting time Read player Input
Handling Computing the new position for every object based on: Behavior Collision Physics Artificial Intelligence
No
Copy the buffer content to the Video Memory in order to display the frame
Yes
Do nothing
DigiPen Game Development C# Webcast Series - Session One, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Overview of C# Programming
DigiPen Institute of Technology
5001 150th Ave NE, Redmond, WA 98052 Phone: (425) 558-0299 www.digipen.edu
Session Two:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Contents
2 C# Types
2.1 Value Types 2.1.1 bool 2.1.2 byte 2.1.3 char 2.1.4 decimal 2.1.5 double 2.1.6 enum 2.1.7 float 2.1.8 int 2.1.9 long 2.1.10 sbyte 2.1.11 short 2.1.12 struct 2.1.13 uint 2.1.14 ulong 2.1.15 ushort 2.2 Reference Types 2.3 void 14 14 15 15 15 16 16 17 17 17 18 18 18 19 19 19 19 20
3 Variables
3.1 Introduction 3.2 Categories of Variables in C# 3.2.1 Static Variables 3.2.2 Instance Variables 3.2.3 Array Elements 3.2.4 Value Parameters 3.2.5 Reference Parameters 3.2.6 Output Parameters 3.2.7 Local Variables 3.3 Default Values 21 21 21 21 21 21 21 22 22 22
5 Functions
5.1 Definition 5.2 Scope 5.3 Calling a Function 5.4 return Statement Example 5.5 Call by Value Function Arguments 5.6 Call by Reference Function Arguments 31 31 31 31 32 33
34
6 Flow Control
6.1 The while Statement 6.2 The do-while Statement 6.3 The for Loop 6.4 The foreach, in Statement 6.5 The jump Statement 6.5.1 break 6.5.2 continue 6.5.3 goto 6.5.4 return 6.6 The switch Statement 6.7 if-else 35 35 36 36 37 37 37 38 38 39 39
7 Classes
7.1 Definition of a Class 7.2 Class Declaration 7.3 Members of a Class 7.4 Constants 7.5 Fields 7.6 Methods 7.7 Properties 7.8 Events 7.9 Indexers 7.10 Operators 7.11 Instance Constructors 7.12 Static Constructors 7.13 Destructors 41 41 41 43 43 44 44 45 45 45 46 46 46
8 Arrays
8.1 Introduction 8.2 Array Types 8.3 Array Creation 8.4 Array Element Access 8.5 Array Members 8.6 Array Initializers 47 47 47 47 47 47
9 Structures
9.1 Definition of a Struct 9.2 Struct Declaration 9.3 Members of a Struct 9.4 Class and Struct Differences 49 49 49 49
10 Miscellaneous
10.1 C# 2.0 10.1.1 The System.Diagnostics Namespace 10.1.2 The System.Drawing Namespace 10.1.3 The System.Windows.Forms Namespace 10.1.4 The System.Collections.Generic Namespace 51 51 51 51 51
10.2 Generics 10.3 The Exception Class 10.4 DirectX 10.4.1 Direct3D 10.4.2 DirectInput 10.4.3 DirectSound 10.4.4 DirectX.AudioVideoPlayback 10.4.5 The Vector2 Class 10.4.6 The Matrix Class
52 52 52 52 53 53 53 53 54
1 C# Programming Overview
1.1 Introduction
C# is a new, simple programming language based on the older programming language called C++. The similarities between C# and C++ are easily notified. This chapter is an overview of the C# programming language. In this chapter we might use some instructions or expressions without explaining their meaning. These materials will be covered in subsequent chapters.
OR
The main function is the entry point of a C# program. All standard C# programs start by executing the content of the main function. However, this program does not do anything because the main function does not have instructions. Facts: An open and close parenthesis is placed after the function name. Parentheses are used to hold the function arguments. The type of the value returned by the function is specified before the function name. Void is a C# type specifying a typeless type. The body of the function is written within the function block specified by the open and close curly braces. The C# programming language is a free format language.
members of the namespace. The hello, world program uses Console.WriteLine as shorthand for System.Console.WriteLine. (For the sake of brevity, most examples in this specification omit the using System; directive.) The Main method is a member of the class Hello. The entry point for an applicationthe method that is called to begin executionis always a static method named Main. The hello, world output is produced using a class library. The language does not itself provide a class library. Instead, it uses a class library that is also used by other programming languages.
1.4 Comments
Two forms of comments are supported: single-line comments and delimited comments. Single-line comments start with the characters // and extend to the end of the source line. Delimited comments start with the characters /* and end with the characters */. Delimited comments may span multiple lines. Comments do not nest. The character sequences /* and */ have no special meaning within a // comment, and the character sequences // and /* have no special meaning within a delimited comment. The example below includes a delimited comment. /* Hello, world program This program writes hello, world to the console */ class Hello { static void Main() { Console.WriteLine(hello, world); } } The following example shows several single-line comments. // Hello, world program // This program writes hello, world to the console // class Hello // any name will do for this class { static void Main() { // this method must be named Main Console.WriteLine(hello, world); } }
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
class hello { static void Main() { DisplayHello();DisplayWorld(); DisplayHelloWorld(); } /* Function definition */ static void DisplayHello() { Console.WriteLine("Hello"); } static void DisplayWorld() { Console.WriteLine("World"); } static void DisplayHelloWorld() { Console.WriteLine("Hello World"); } }
Output
Facts: The program contains three user functions. Functions need to be defined before being used or executed. The main function contains three function calls or three statements. User functions are declared outside the main function. Functions can be called many times. The program starts by executing the first instruction in Main, which is the DisplayHello function. When the DisplayHello function is called, the execution flow changes to the first instruction within the function definition of DisplayHello. When the last instruction (which is also the first instruction) of the function DisplayHello is executed, the execution returns to the instruction right after the function call, which is DisplayWorld.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
class hello { static void Main( ) { Console.WriteLine("{0}",Add(3,5)); } /* Function definition */ static int Add(int x, int y) { return x+y; } }
Output
Facts: int is a C# type specifying whole or integral numbers. int means integer. The function prototype specifies that the function takes two integer arguments and returns an integer. When a function has more than one argument, a comma is used to separate the arguments. When a function with arguments is called, the arguments are received as parameters by the function where the functions instructions are specified. When arguments are passed to the parameters, the order of the arguments is respected. In our case, x would be equal to 3 and y would be equal to 5. The last statement of the function definition returns the result of the arithmetic expression x + y. x and y are called variables. A variable is a name assigned to a data storage location. The variables x and y are defined in the parameter list of the function: (int x, int y). In C#, a variable must be defined before it can be used. A variable definition specifies its name and type. The compiler uses the type in order to know how much memory to allocate.
1.7 Variables
class add { static void Main( ) { int i,j; i=3; j=5; Console.WriteLine("{0}", Add(i,j)); } static int Add(int x, int y) { return x+y; } }
Output
Facts: Two integer variables i and j are declared and defined. By declaration, we mean that the rest of the function main knows about the presence and type of i and j. DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
class test { static void Main( ) { int x1=2,y1=1,x2=7,y2=3; Console.WriteLine("{0}", Distance(x1,y1,x2,y2)); } static double Distance(int x1, int y1, int x2, int y2) { int deltaX, deltaY; deltaX=x2-x1; deltaY=y2-y1; return Math.Sqrt(deltaX*deltaX + deltaY*deltaY); } }
In other words, the scope and type of i and j is within the body of function main. Then, an assignment operator is used in order to assign the value 3 and 5 to i and j respectively. The function Add is used by having two variables as arguments, while in the previous example the arguments were constants.
Output
5.3851648071345
Facts: Four variables of type integer are declared, defined, and assigned a value. int x1=2,y1=1,x2=7,y2=3; In C#, you can assign an initial value to a variable during the declaration. The function Distance takes four integer arguments and returns a value of type double. The C# double type allows the variable to have a value with fraction. A value with fraction is made from an integral part followed by a decimal point followed by the precision DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview (5.3851648071345). The function Distance requires a square root calculation. The square root function Sqrt is defined in the namespace called Math. The function Distance declares and defines six variables: x1,y1,x2,y2,deltaX, and deltaY. Remember that variables declared inside a function are only available while executing the function. deltaX and deltaY are used to hold the difference between the first and the second point. The statement deltaX=x2-x1; subtract x1 from x2 then assign the result to deltaX. In C# arithmetic, the multiplication * operator is evaluated from left to right. The multiplication operator has a higher order of precedence than the addition operator. This is why deltaX*deltaX is evaluated first to 25. Next deltaY*deltaY is evaluated to 4. Then 25 and 4 are added evaluating to 29. Then the square root of 29 is evaluated to 5.3851648071345.
10
Facts: i>100 is the Boolean expression that evaluates to true or false. If the expression is true, then the statement (or the block of statements enclosed between an opening and a closing curly braces) following the condition is executed. If the expression is false then the statement following the condition is skipped. The statement following the else is executed only when the conditional expression is false. In other words, only one of the WriteLine statements will be executed. The conditional statement starts with the keyword if followed by an opening parenthesis, followed by a Boolean expression, followed by a closing parenthesis, followed by an instruction: if(Boolean expression) instruction; Notice that there is no semicolon after the closing parenthesis of the Boolean expression because the conditional statement has not ended yet.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
11
1.10 Loops
class test { static void Main() { int i; for(i=0;i<10;i=i+1) Console.Write("{0} ",i); Console.Write("\n"); i=0; while(i<10) { Console.Write("{0} ",i); i=i+1; } } }
Output
0123456789 0123456789
Facts: The for loop form is: f o r ( e x p r e s s i o n 1 ; e x p r e s s i o n 2 ; e x p r e s s i o n 3 ) statement The loop is initialized through expression1 i=0; Expression2 specifies the test made before each iteration i<10; If expression2 is true, the statement Console.Write({0} ,i); is executed, then expression 3 i=i+1 is executed. The loop iterates until expression2 is false. If expression2 is false, the for loop will exit, and the control is transferred to the statement following the statement. Expression3 is evaluated after each iteration. Any or all of the three for expressions may be omitted, but the semicolon must remain. The while loop form is: while (expression) statement If expression i<10 is true, the statement is executed until expression becomes false. In our case the statement is made from a block enclosed between curly braces: { Console.WriteLine({0} ,i); i=i+1; } If expression is false, the execution resumes at the following statement. In our case, the following statement is the end of the program. The expression is evaluated before the statement is executed. When the expression is false from the first time, the statement will never be executed.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
12
Output
0123456789
Facts: It is a collection of variables of the same type that are referred to by the same name. In our case int[] A = new int[10]; reserved 10 integers. Array elements are accessed through an index; in our case the index is i. The first element is accessed by index 0 A[0]. The highest address corresponds to the last element and it is accessed by index (total number of elements 1); in our case it is A[9]. The amount of storage required to hold an array depends on the type and the total number of elements; in our case it is 10 * 4=40, since each integer is 4 bytes. The C# compiler does not perform index range checking. The array element is accessed by indexing the array name. It is done by writing the index enclosed between brackets placed after the array name. arrayName[index] A[i]=i;
1.12 Structure
class test { struct point { public int x; public int y; }; static void Main() { point p1, p2; p1.x=2; p1.y=1; p2.x=7; p2.y=3; Console.WriteLine(Distance(p1,p2));/* will print 5.3851648071345 */ } static double Distance(point p1,point p2) { int deltaX, deltaY; deltaX=p2.x - p1.x; deltaY=p2.y - p1.y; return Math.Sqrt(deltaX*deltaX + deltaY*deltaY); } }
Facts: A structure is an object consisting of a sequence of named members of various types. It is a collection of variables referenced under one name. DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Chapter One: Overview Programming The collection of variables is logically related. It provides a convenient way in order to keep related information together.
13
A structure is declared by typing the keyword struct, followed by the structure name, followed by the structure members enclosed between curly braces; for example: struct point { public int x, y; } Once a structure is declared, variables having the structure type could be declared by typing the structure name followed by the variable name; for example: int deltaX; The dot . operator is used to access the structure member. First write the structure variable name, followed by a dot, followed by a member; for example: deltaX=p2.x - p1.x;
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
14
2 C# Types
2.1 Value Types
In C#, a value type can be either a struct or an enumeration. C# contains a set of predefined struct types called the simple types. These simple types are identified through reserved words. All value types implicitly inherit form a class called object. Also, no type can derive from a value type. It is not possible for a value type to be null (null means nothing or no value). Assigning a variable of a value type creates a copy of the value. This is null different from assigning a variable of a reference type, which copies the reference and not the object identified by the reference.
2.1.1 bool
The bool type represents boolean quantities. There can be two possible values of type bool: true and false. There is no standard conversion between bool and other types. Such conversions are accomplished by comparing an integral value to zero or comparing an object to null. A boolean value can be assigned to a bool variable, for example: bool MyBool = true; You can also assign an expression that evaluates to a bool variable, for example: bool b = (i > 66); Conversions No conversion exists between the bool type and other types. For example, the following if statement: int i = 101; if (i) { } is not allowed in C#. To test an int type, you have to explicitly compare it to a value, as follows: int i = 13; if (i == 13) { // do something } Example: In this example, you enter a character from the keyboard and the program checks if the input character is a letter. public class test { static void Main() { Console.Write(Enter a character: ); char ch = (char) Console.Read(); if (Char.IsLetter(ch)) Console.WriteLine(It is an alphabetic character.); DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview else Console.WriteLine(It is not an alphabetic character.);
15
2.1.2 byte
The byte keyword denotes an integral type that stores values ranging from 0 to 255. Its size is 8-bit. A byte can be declared and initialized as follows: byte b = 117; In the preceding declaration, 117 is implicitly converted from int to byte. If the integer literal exceeds the range of byte, a compilation error will occur, as with the following assignment statement: byte c = a + b; // Error: conversion from int to byte To fix this problem, use an explicit cast: byte c = (byte)(a + b); // OK
2.1.3 char
char is used to declare a Unicode character in the range 0 to 65535. Unicode characters are 16-bit characters used to represent most of the written languages throughout the world. The following statement declares a char variable and initializes it with the character D: char c = D;
2.1.4 decimal
decimal denotes a 128-bit data type. The decimal type has a greater precision and a smaller range than the floating-point type, which makes it suitable for financial and monetary calculations. To make sure that a numeric real number is treated as a decimal, use the suffix m or M: decimal dec = 710.88m; Without the suffix m, the number is treated as a double, and the expression generates a compilation error. Example: public class test { static void Main () { decimal dec = 12.4m; int i = 33; Console.WriteLine(dec * i); } }
Formatting Decimal Output You can format the results by using the String.Format method, or through the Console.Write method, which calls String.Format(). The currency format is specified using the standard currency format string C or c. Example: In this example, the output is formatted using the currency format string. public class test DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
16
static void Main () decimal dec1 = 0.987m; decimal dec2 = 5454335566m; Console.WriteLine({0:C}, dec1); Console.WriteLine({0:C}, dec2);
2.1.5 double
double denotes a simple type that stores 64-bit floating-point values. By default, a real numeric literal on the right-hand side of the assignment operator is treated as a double. However, if you want an integer number to be treated as a double, use the suffix d or D. double x = 44D; Numeric integral types and floating-point types can be mixed in an expression. In this case, the integral types are converted to floating-point types. Example: class test { static void Main() { float f = 8.66f; int i = 123; double d = 22.1E+2; Console.Write({0}, f + i + d); } }
2.1.6 enum
enum is used to declare an enumeration, which is distinct type consisting of a set of constants called the enumerator list. Every enumeration type has an underlying type, which can be any integral type except char. The default type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1. For example: enum WeekDays {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; In this enumeration, Sun is 0, Mon is 1, and so forth. Enumerators can have initializers overriding the default values, as the following example shows: enum WeekDays {Sun=1, Mon, Tue, Wed, Thu, Fri, Sat}; In this enumeration, the sequence starts from 1. An enum En has a default value, which is the value produced by the expression (En)0. The enumeration type specifies how much storage is allocated. However, an explicit cast is needed to convert from enum type to an integral type. Example: In this example, an enumeration, Days, is declared. Two enumerators are explicitly converted to int and assigned to int variables. DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview public class test { enum WeekDays {Sun=1, Mon, Tue, Wed, Thu, Fri, Sat}; static void Main() { int i1 = (int) WeekDays.Tue; int i2 = (int) WeekDays.Thu; Console.WriteLine(Tuesday is day {0}, i1); Console.Write(Thursday is day {0}, i2); } } Output: Tuesday is day 3 Thursday is day 5
17
2.1.7 float
float denotes a type that can store 32-bit floating-point values. A real numeric literal on the right-hand side of the assignment operator is treated by default as a double. Therefore, to initialize a float, use the suffix f or F, for example: float f = 68.77F; You will get a compilation error if you do not use the suffix because you are attempting to store a double value into a floating point variable. You can mix numeric integral types and floating-point types in an expression. In this case, the integral types are converted to floating-point types. Example: class test { static void Main() { int i = 14; float f = 68.25f; Console.Write({0}, i-f); } }
2.1.8 int
int denotes an integral type that stores 32-bit values. It ranges from -2,147,483,648 to 2,147,483,647. The type int is declared and initialized like this: int i = 441;
2.1.9 long
long denotes an integral type that stores 64-bit values. It ranges from 9,223,372,036,8 54,775,808 to 9,223,372,036,854,775,807. The type long is declared and initialized like this: long myLong = 23940043; When an integer literal has no suffix, its type is the first of the following types in which its value can fit: int, uint, long, ulong. The suffix L can be used with the long type like this: long myLong = 990085665543L; When you use the suffix L or l, the literal integers type is either long or ulong according to its size. A predefined implicit conversion exists from long to float, double, or decimal. In other cases, a cast must be DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview used. For example, the following statement will produce a compilation error without an explicit cast: int i = 21L;
18
There is an implicit conversion from sbyte, byte, short, ushort, int, uint, or char to long. Also, there is no implicit conversion from floating-point types to long. For example, the following statement generates an error: long l = 31.23;
2.1.10 sbyte
sbyte denotes an integral type that stores signed 8-bit integer values, ranging from -128 to 127. An sbyte can be declared and initialized like this: sbyte mySbyte = 100; 100 is implicitly converted from int to sbyte. If the integer literal exceeds the range of sbyte, a compiler error will occur. A predefined implicit conversion exists from sbyte to short, int, long, float, double, or decimal. Also, there is no implicit conversion from floating-point types to sbyte.
2.1.11 short
short denotes an integral data type that stores signed 16-bit integer values, ranging from -32,768 to 32,767. A short is declared and initialized like this: short s = 30201; 30201 is implicitly converted from int to short. If the integer literal does not fit into a short storage location, a compiler error will occur. A predefined implicit conversion exists from short to int, long, float, double, or decimal. You cannot implicitly convert non-literal numeric types of larger storage size to short. Also there is no implicit conversion from floating-point types to short.
2.1.12 struct
A struct is a value type. It can contain constructors, constants, fields, methods, properties, indexers, operators, and nested types. The struct type is suitable for representing objects such as Point, Rectangle, and Color. When creating a struct object using the new operator, it gets created, and the appropriate constructor is called. Structs can be instantiated without using the new operator. If you do not use the new operator, the fields will remain unassigned and the object cannot be used until all of the fields are initialized. You cannot declare a class using the keyword struct. Classes and structs are semantically different. A struct is a value type, while a class is a reference type. Example: public struct Point { public int x, y; } Example: This example creates a Point object without using the new operator. public struct Point { public int x, y; } class test { static void Main() { DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
19
2.1.13 uint
uint denotes an integral type that stores unsigned 32-bit integer values, ranging from 0 to 4,294,967,295. An uint can be declared and initialized like this: uint myUint = 4294967288; The suffix u or U can be used, like this: uint myUint = 112U; If you use the suffix U or u, the literal type is determined to be either uint or ulong according to its size. In this example, it is uint. A predefined implicit conversion exists from uint to long, ulong, float, double, or decimal. For example: float myFloat = 4294967289; Also, there exists a predefined implicit conversion from byte, ushort, or char to uint. Otherwise you must use a cast. There is no implicit conversion from floating-point types to uint.
2.1.14 ulong
The ulong keyword denotes an integral type that stores unsigned 64-bit integer values, ranging from 0 to 18,446,744,073,709,551,615. A ulong is declared and initialized like this: ulong myUlong = 92854775806; When using L or l as a suffix, the type of the literal integer will be either long or ulong according to its size. There is an implicit conversion from ulong to float, double, or decimal, but there is no implicit conversion from ulong to any integral type.
2.1.15 ushort
ushort denotes an integral data type that stores unsigned 16-bit integer values, ranging from 0 to 65,535. ushort can be declared and initialized like this: ushort myUShort = 65535; 65535 is implicitly converted from int to ushort. A compiler error will occur if the integer literal exceeds the range of ushort. A predefined implicit conversion exists from ushort to int, uint, long, ulong, float, double, or decimal. Also, there is a predefined implicit conversion from byte or char to ushort. Otherwise a cast must be used. There is no implicit conversion from floating-point types to ushort.
20
The string type inherits directly from class object. Interface Types An interface defines a contract. A class implementing an interface must adhere to its contract. Array Types An array is a data structure containing a number of variables, which are accessed through indices. The variables contained in an array are called the elements of the array. They are all of the same type, and this type is called the element type of the array.
2.3 void
When used as the return type for a method, void specifies that the method does not return a value. void is not allowed in a methods parameter list. A method with no parameters and returning no value is declared as follows: void MyMethod();
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
21
3 Variables
3.1 Introduction
In C#, a variable represents a storage location. A variable has a type that determines what values can be stored in this variable. Because C# is a type-safe language, the C# compiler guarantees that values stored in variables are always of the appropriate type. The value of a variable is changed through the assignment operator. The value of a variable is also changed through the use of the ++ and -- operators. A variable must be definitely assigned before its value can be obtained: variables are either initially assigned or initially unassigned. An initially assigned variable has a well-defined initial value. An initially unassigned variable has no initial value.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
22
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
23
The final result of an expression cannot be one of the following: A namespace. A type. A method group. An event access.
These categories are intermediate constructs. They are only permitted in certain contexts. Values of Expressions If the expression denotes a property access, an indexer access, or a variable, the value of the property, indexer, or variable is implicitly substituted: The value of a variable is simply the value currently stored in the storage location identified by the variable. The value of a property access expression is obtained by invoking the get-accessor of the property. The value of an indexer access expression is obtained by invoking the get-accessor of the indexer.
4.2 Operators
Expressions are constructed from operands and operators. Operators of an expression indicate which operations to apply to the operands. We can find three types of operators: 1. Unary Operators: The unary operators take one operand. They use either prefix notation (i) or postfix notation (i++). 2. Binary Operators: The binary operators take two operands. They all use infix notation (i + j). 3. Ternary Operator: There exists only one ternary operator, ?:. The ternary operator takes three operands and uses infix notation (z? x: y). Operands in an expression are evaluated from left to right. Certain operators can be overloaded. This permits user-defined operator implementations to be specified for operations where one or both of the operands are of a DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview user-defined class or struct type.
24
Operator Precedence and Associativity When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. For example, the expression a + b * c is evaluated as a + (b * c) because the * operator has higher precedence than the + operator. The precedence of an operator is established by the definition of its associated grammar production. When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed: All binary operators are left-associative, except for the assignment operators, meaning that operations are performed from left to right. Assignment operators and conditional operator (?:) are right-associative, which means that operations are performed from right to left. Precedence and associativity can be controlled using parentheses. Operator Overloading User-defined implementations can be introduced by including operator declarations in classes and structs. User-defined operator implementations always take precedence over predefined operator implementations only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered. The overloadable unary operators are: + ! ~ ++ -true false The overloadable binary operators are: + - * / % & | ^ << >> == != > < >= <=
When a binary operator is overloaded, the corresponding assignment operator (if any) is also implicitly overloaded. For example, an overload of operator + is also an overload of operator +=. The assignment operator (=) cannot be overloaded. An assignment performs a bit-wise copy of a value into a variable. Element access, such as Ar[x], is not an overloadable operator. User-defined operator declarations always require at least one of the parameters to be of the class or struct type that contains the operator declaration. User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. For example, the / operator is always a binary operator, always has the precedence level specified in, and is always left-associative.
Statements contained in function members are executed through function member invocations. The argument list of a function member invocation provides actual values or variable references for the parameters of the function member. The new Operator The new operator is used to create new instances of types. There are three forms of new expressions: DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
25
1. Object creation expressions are used to create new instances of class types and value types. 2. Array creation expressions are used to create new instances of array types. 3. Delegate creation expressions are used to create new instances of delegate types. The new operator implies creation of an instance of a type. Instances of value types require no additional memory beyond the variables in which they reside.
For each of these operators, the result is simply the value of the operand. The - Operator The predefined negation operators are: 1. Integer negation: int operator (int x); long operator (long x); The result is computed by subtracting x from zero. 2. Floating-point negation: float operator (float x); double operator (double x); The result is the value of x with its sign inverted. 3. Decimal negation: decimal operator (decimal x); The result is computed by subtracting x from zero. Decimal negation is equivalent to using the unary minus operator of type Decimal. 4. Logical negation operator: There is only one predefined logical negation operator: bool operator !(bool x); This operator computes the logical negation of the operand: if the operand is true, the result is false. If the operand is false, the result is true. 5. Bitwise complement operator: The bitwise complement operators are: int operator ~(int x); uint operator ~(uint x); long operator ~(long x); ulong operator ~(ulong x); 6. Prefix increment and decrement operators: DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
26
Pre-increment-expression: ++ unary-expression Pre-decrement-expression: -unary-expression The value returned by the operator becomes the result of the operation. The ++ and -- operators also support postfix notation. Cast Expressions A cast-expression is used to explicitly convert an expression to a given type. Example: ( type ) unary-expression
Floating-point multiplication: float operator *(float x, float y); double operator *(double x, double y); Decimal multiplication: decimal operator *(decimal x, decimal y); Division Operator Integer division: int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);
Floating-point division: float operator /(float x, float y); double operator /(double x, double y); Decimal division: decimal operator /(decimal x, decimal y); Remainder Operator DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview Integer remainder: int operator %(int x, int y); uint operator %(uint x, uint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y); Floating-point remainder: float operator %(float x, float y); double operator %(double x, double y); Decimal remainder: decimal operator %(decimal x, decimal y); Addition Operator Integer addition: int operator +(int x, int y); uint operator +(uint x, uint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y); Floating-point addition: float operator +(float x, float y); double operator +(double x, double y); Decimal addition: decimal operator +(decimal x, decimal y); Enumeration addition: E operator +(E x, U y); E operator +(U x, E y); String concatenation: string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y); Subtraction Operator Integer subtraction: int operator (int x, int y); uint operator (uint x, uint y); long operator (long x, long y); ulong operator (ulong x, ulong y); Floating-point subtraction: float operator (float x, float y); double operator (double x, double y); Decimal subtraction: decimal operator (decimal x, decimal y); Enumeration subtraction: U operator (E x, E y); E operator (E x, U y); DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
27
28
Operation Result
x == y x != y x < y true if x is equal to y, false otherwise true if x is not equal to y, false otherwise true if x is less than y, false otherwise DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
29
true if x is greater than y, false otherwise true if x is less than or equal to y, false otherwise true if x is greater than or equal to y, false otherwise
The as Operator This operator is used to explicitly convert a value to a given reference type using a reference conversion or a boxing conversion. The as operator never throws an exception. Instead, if the indicated conversion is not possible, the resulting value is null.
30
operator precedence is classified as a conditional-or-expression. A boolean-expression is required to be of a type that can be implicitly converted to bool or of a type that implements operator true. If neither requirement is satisfied, a compile-time error occurs. When a boolean expression is of a type that cannot be implicitly converted to bool but does implement operator true, then following evaluation of the expression, the operator true implementation provided by that type is invoked to produce a bool value.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
31
5 Functions
5.1 Definition
The definition of a function includes the return type, the function name, the parameters list, and the function body. The function body is enclosed between an opening and a closing brace. Example: int add(int a, int b) { return a + b; } The parameters a and b receive the values of the arguments when the function is called. Example: int n; n = add(4,5);
5.2 Scope
The code found in a function is private to the function and cannot be accessed by any statement from another function. Function code is accessed only through function call. A C# program starts from the function Main. All functions have a file scope. Parameters and variables declared inside the function have function scope. They are created when the function is entered and destroyed when the function ends. Static variables declared inside the function have a function scope, but they retain their values between function calls.
struct box { public float left; public float top; public float right; public float bottom; }; static float Maximum(float v1, float v2) { DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview if(v1>v2) return v1; else return v2;
32
static float Minimum(float v1, float v2) { if(v1<v2) return v1; else return v2; } static bool Intersect(float v1, float v2) { if(v1-v2<=0) return true; else return false; } static void Main() { box b1, b2; Console.WriteLine(Enter the left, top, right, and bottom coordinates of b1:); /* the input from keyboard is stored in the structure members */ b1.left = Int32.Parse(Console.ReadLine()); b1.top = Int32.Parse(Console.ReadLine()); b1.right = Int32.Parse(Console.ReadLine()); b1.bottom = Int32.Parse(Console.ReadLine()); Console.WriteLine(Enter the left, top, right, and bottom coordinates of b2:); b2.left = Int32.Parse(Console.ReadLine()); b2.top = Int32.Parse(Console.ReadLine()); b2.right = Int32.Parse(Console.ReadLine()); b2.bottom = Int32.Parse(Console.ReadLine()); if(Intersect(Maximum(b1.left,b2.left)-Minimum(b1.right,b2.right), Minimum(b1.bottom,b2.bottom)-Maximum(b1.top,b2.top))) Console.WriteLine(b1 and b2 intersect\n); else Console.WriteLine(b1 and b2 do not intersect\n); } }
33
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
34
static void Main() { int i; Console.Write(Enter an integer value: ); i = Int32.Parse(Console.ReadLine()); Console.WriteLine(The absolute value of {0} is {1}, i, Absolute(i)); } /* the function receives an integer and returns its absolute value */ static int Absolute(int i) { return i>=0? i:-i; }
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
35
6 Flow Control
6.1 The while Statement
The while statement executes a block of statements repeatedly until a specified expression evaluates to false. It has the form while (expression) statement where expression is an expression that can be implicitly converted to bool or a type that contains overloading of the true and false operators. The expression is used to test the loop-termination criteria. statement is the statement(s) that will be executed. A while loop executes zero or more times because the test of expression takes place before each execution of the loop. A while loop can terminate when a break, goto, return, or throw statement transfers control outside the loop. To pass control to the next iteration without exiting the loop, use the continue statement. Example: using System; class Test { static void Main() { int i = 16; while (n > 0) { Console.WriteLine(Another value of i: {0}, i); i -= 4; } } }
} Example:
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview using System; public class Test { static void Main () { int i = 16; do { Console.WriteLine(Another value of i: {0}, i); i -= 4; } } while(i > 18);
36
In the preceding example, although the condition evaluates initially to false, the loop will be executed once.
37
The statements continue to execute for each element in the array. Control is transferred to the next statement following the foreach block after the iteration has been completed for all the elements. When used with an array, the foreach statement repeats the embedded statement(s) for each element in the array. Example: using System; class test { static void Main() { int[] ar = new int [] {0,-1,2,-3,4,-5,6,-7,8,-9}; foreach (int i in ar) { if (i < 0) Console.WriteLine(This is a negative number); else Console.WriteLine(This is a positive number); } } }
6.5.1 break
The break statement terminates the closest enclosing loop or conditional statement in which it appears. Control is passed to the statement that follows the terminated statement, if any. The break statement takes the form: break; Example: using System; class Test { static void Main() { for (int i = 15; i >= 0; i--) { if (i == 10) break; Console.Write(i); } } }
6.5.2 continue
The continue statement passes control to the next iteration of the enclosing iteration statement in which it appears. It takes the form: continue; Example: using System; class Test { static void Main() { DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview for (int i = 13; i > 1; i--) { if (i > 2) continue; Console.WriteLine(The current value is : {0}, i); }
38
6.5.3 goto
The goto statement transfers control directly to a labeled statement. It can be one of the following forms: goto identifier; goto case constant-expression; goto default; where identifier is a label and constant-expression is switch-case label. identifier indicates a label located in the current body, the same scope, or an enclosing scope of the goto statement. goto is commonly used to transfer control to a switch-case label. goto is useful to get out of nested loops. A warning message can be issued if the label has never been referenced in the program. Example: using System; class test { static void Main() { int i = 0; do { if (i == 8) goto Label1; else i++; } while (true); } Label1: Console.Write(i);
6.5.4 return
The return statement terminates execution of the method in which it appears and returns control to the calling method. If the method is of the type void, the return statement can be omitted. The return statement has the form: return [expression]; where expression is the value returned by a method. expression is not used with methods of the type void. Example: class Test { static int AddInt(int i1, int i2) DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
39
int j = i1 + i2; return j; static void Main() n1 = 120; n2 = 125; sum = AddInt(n1,n2);
6.7 if-else
The if statement is a control statement that executes a block of code if an expression evaluates to true. It has the form: if (expression) statement1 DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
40
where expression is an expression that can be converted to bool, statement1 is the statement(s) to be executed if expression is true, and statement2 is the statement(s) to be executed if expression is false. If expression is true, statement1 is executed. If the optional else clause exists and expression evaluates to false, statement2 is executed. After executing the if statement, control is transferred to the next statement. If any of the two results of the if statement (true or false) results in executing more than one statement, multiple statements can be conditionally executed by including them into blocks. The statement(s) to be executed upon testing the condition can be of any kind, including another if statement nested into the original if statement. In nested if statements, the else clause belongs to the last if that does not have a corresponding else. ill be displayed if the condition (x > 10) evaluates to false. x Example: using System; public class Test { static void Main() { Console.Write(Enter something from the keyboard ); char ch = (char) Console.Read(); if (Char.IsLetter(ch)) if (Char.IsLower(ch)) Console.WriteLine(What you entered is a lowercase character.); else Console.WriteLine(What you entered is an uppercase character.); else Console.WriteLine(What you entered is not a character.); } } Example: using System; public class Test { static void Main() { Console.Write(Press a key: ); char ch = (char) Console.Read(); if (Char.IsUpper(ch)) Console.WriteLine(You pressed an uppercase character.); else if (Char.IsLower(c)) Console.WriteLine(You pressed a lowercase character.); else if (Char.IsDigit(c)) Console.WriteLine(You pressed a digit.); else Console.WriteLine(What You pressed is not alphanumeric.);
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
41
7 Classes
7.1 Definition of a Class
A class is a data structure. It may contain data, functions, and nested types. Data members include constants and fields. Function members include methods, operators, events, properties, indexers, instance constructors, destructors and static constructors. A class support inheritance, which is a mechanism that allows a derived class to extend and specialize a base class.
Game Web Cast Project: C# Programming Overview from the direct base class. Class members are divided into the following categories: 1. Fields are the class variables. 2. Constants represent constant values associated with the class. 3. Methods implement the computations and actions that can be performed by the class. 4. Properties define characteristics associated with reading and writing those characteristics. 5. Indexers permit instances of the class to be indexed like arrays. 6. Events define notifications that can be generated by the class. 7. Instance constructors implement class initialization. 8. Operators define the expression operators that would be applied to instances of the class. 9. Static constructors implement the actions required to initialize the class itself. 10. Destructors implement the actions to be performed before instances of the class are deleted. 11. Types represent local types of the class.
42
Members that can contain executable code are known as the function members of the class. The function members of a class are the events, operators, methods, properties, indexers, instance constructors, static constructors, and destructors of that class. A class-declaration creates a new declaration space, and the class-member-declarations immediately contained by the class-declaration introduce new members into this declaration space. Rules that apply to class-memberdeclarations are: 1. Instance constructors, destructors and static constructors should have the same name as the immediately enclosing class. 2. The name of a constant, property, type, field, or event should differ from the names of all other members declared in the same class. 3. The name of a method should differ from the names of all other non-methods declared in the same class. 4. The signature of a method should differ from the signatures of all other methods declared in the same class. 5. The signature of an instance constructor should differ from the signatures of all other instance constructors declared in the same class. 6. The signature of an indexer should differ from the signatures of all other indexers declared in the same class. 7. The signature of an operator should differ from the signatures of all other operators declared in the same class. 8. The inherited members of a class are not part of the declaration space of a class. Therefore, a derived class is allowed to declare a member with the same name or signature as an inherited member. Inheritance A class inherits the members of its direct base class. It implicitly contains all members of its direct base class, except for the instance constructors, destructors and static constructors of the base class. Inheritance is transitive. If C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A. A derived class extends its direct base class. It can add new members to those it inherits, but it cannot remove the definition of an inherited member. Instance constructors, destructors, and static constructors are not inherited. A derived class can hide inherited members by declaring new members with the same name or signature. A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. This enables classes to exhibit polymorphic behavior wherein the actions performed by a function member invocation vary depending on the run-time type of the instance through which the function member is invoked. Access Modifiers It is a compile-time error to specify more than one access modifier, except for the protected internal combination. When a class-member-declaration does not include any access modifiers, private is assumed. A class-memberdeclaration can have any one of the five possible kinds of declared accessibility: public, protected internal, protected, internal, or private. Static and Instance Members DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
43
Members of a class are either static members or instance members. Static members belong to classes, and instance members belong to objects (instances of classes). When a method, event, field, property, operator, or constructor declaration includes a static modifier, it declares a static member. Additionally, a constant or type declaration implicitly declares a static member. When a method, event, field, property, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. Nested Types A type declared within a class or struct is called a nested type. A type that is declared within a compilation unit or namespace is called a non-nested type. Remark: this within a nested type cannot be used to refer to instance members of the containing type. Access to Private and Protected Members of the Containing Type A nested type has access to all of the members that are accessible to its containing type, including members of the containing type that have private and protected declared accessibility. Reserved Member Names For each member declaration that is a property, event, or indexer, the implementation must reserve two method signatures based on the kind of the member declaration, its name, and its type. It is a compile-time error for a program to declare a member whose signature matches one of these reserved signatures. The reserved names do not introduce declarations, thus they do not participate in member lookup. Destructor declaration causes a signature to be reserved. For a property P of type T, the following signatures are reserved: T T get_P(); void set_P(T value); Both signatures are reserved, even if the property is read-only or write-only. For an event E of delegate type T, the following signatures are reserved: T void add_E(T handler); void remove_E(T handler); For an indexer of type T with parameter-list L, the following signatures are reserved: T get_Item(L); void set_Item(L, T value); Both signatures are reserved, even if the indexer is read-only or write-only. For a class containing a destructor, the following signature is reserved: void Finalize();
7.4 Constants
A constant is a class member that represents a constant value that can be computed at compile-time. A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature.
7.5 Fields
A field represents a variable associated with an object or class. A field-declaration introduces one or more fields of a given type. It declares that multiple fields are the same as multiple declarations of single fields with the same attributes, modifiers, and type. DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
44
Static and Instance Fields When a field declaration includes a static modifier, the fields introduced are static fields. When no static modifier is present, the fields introduced are instance fields. A static field is not part of a specific instance. There is only one copy of a static field for the associated application domain. An instance field belongs to an instance. Every instance of a class contains a separate set of all instance fields of the class. Readonly Fields When a field-declaration includes a readonly modifier, the fields are readonly fields. Direct assignments to readonly fields can only occur as part of the declaration or in an instance constructor (for readonly non-static fields) or static constructor (for readonly static fields) in the same class. Attempting to assign to a readonly field or passing it as an out or ref parameter in any other context results in a compile-time error.
7.6 Methods
A method is a member that implements a computation or action that can be performed by an object or class. Methods are declared using method-declarations. The return-type of a method declaration specifies the type of the value computed and returned by the method. The return-type is void if the method does not return a value. The member-name specifies the name of the method. Method Parameters The methods formal-parameter-list declares the parameters of a method, if any exist. Value Parameters A parameter declared with no modifiers is a value parameter. It corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation. When a formal parameter is a value parameter, the corresponding argument in a method invocation must be an expression of a type that is implicitly convertible to the formal parameter type. A method is permitted to assign new values to a value parameter. Reference Parameters A parameter declared with a ref modifier is a reference parameter. It does not create a new storage location. A reference parameter represents the same storage location as the variable given as the argument in the method invocation. A variable must be definitely assigned before it can be passed as a reference parameter. Within a method, a reference parameter is always considered definitely assigned. Output Parameters A parameter declared with an out modifier is an output parameter. It does not create a new storage location. An output parameter represents the same storage location as the variable given as the argument in the method invocation. A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned. Within a method an output parameter is considered initially unassigned and must be definitely assigned before its value is used. Every output parameter of a method must be definitely assigned before the method returns. Static and Instance Methods When a method declaration includes a static modifier, the method is said to be a static method. When no static modifier is present, the method is said to be an instance method. A static method does not operate on a specific instance, and it is a compile-time error to refer to this in a static method. On the other hand, an instance method operates on a given instance of a class, and this instance can be accessed as this.
7.7 Properties
A property is a member that provides access to a characteristic of an object or a class. The length of a string, the caption of a window, the name of a customer, and the size of a font are all examples of a property. Properties are a natural extension of fields and do not denote storage locations. The type of a property declaration specifies the type of the property introduced by the declaration, and the member-name specifies the name of the property. The type of a property must be at least as accessible as the property itself. DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
45
Static and Instance Properties When a property declaration includes a static modifier, the property is said to be a static property. When no static modifier is present, the property is said to be an instance property. A static property is not associated with a specific instance. On the other hand, an instance property is associated with a given instance of a class, and this instance can be accessed as this in the accessors of the property. Virtual, Sealed, Override, and Abstract Accessors A virtual property declaration specifies that the accessors of the property are virtual. The virtual modifier applies to both accessors of a read-write property. It is not possible for only one accessor of a read-write property to be virtual. An abstract property declaration specifies that the accessors of the property are virtual, but it does not provide an actual implementation of the accessors. Non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Abstract property declarations are only permitted in abstract classes. The accessors of an inherited virtual property can be overridden in a derived class by including a property declaration that specifies an override directive. An overriding property declaration may include the sealed modifier. The accessors of a sealed property are also sealed. Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods.
7.8 Events
An event is a member that enables an object or class to provide notifications. Clients can attach executable code for events by supplying event handlers. An event can be used as the left hand operand of the += and -= operators. Static and Instance Events When an event declaration includes a static modifier, the event is said to be a static event. When no static modifier is present, the event is said to be an instance event. A static event is not associated with a specific instance. On the other hand, an instance event is associated with a given instance of a class, and this instance can be accessed as this in the accessors of the event.
7.9 Indexers
An indexer is a member that enables an object to be indexed in the same way as an array. An indexer element is not classified as a variable; therefore, it is not possible to pass an indexer element as a ref or out argument. The formal parameter list of an indexer defines the signature of the indexer, which consists of the number and types of its formal parameters. The element type and names of the formal parameters are not part of an indexers signature. The signature of an indexer must differ from the signatures of all other indexers declared in the same class. Indexers and properties are conceptually similar, but they differ in many ways. When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer.
7.10 Operators
An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. There are three categories of overloadable operators: 1. Unary operators. 2. Binary operators. 3. Conversion operators. An operator declaration must include both a public and a static modifier. When an operator declaration includes an extern modifier, the operator is said to be an external operator. For all non-external operators, the operatorbody consists of a block which specifies the statements to execute when the operator is invoked. The parameter(s) of an operator must be value parameters. The signature of an operator must differ from the signatures of all other operators declared in the same class. All types referenced in an operator declaration DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
46
must be at least as accessible as the operator itself. When the same modifier appears multiple times in an operator declaration, it results in a compile-time error. Each operator category imposes additional restrictions, as described in the following sections. Like other members, operators declared in a base class are inherited by derived classes.
7.13 Destructors
A destructor is a member that implements the actions required to destruct an instance of a class. Destructors are not inherited. Thus, a class has no destructors other than the one that may be declared in it. Since a destructor is required to have no parameters, it cannot be overloaded. Thus, a class can have, at most, one destructor. Destructors are invoked automatically, and cannot be invoked explicitly. An instance becomes eligible for destruction when it is no longer possible for any code to use the instance. Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. When an instance is destructed, the destructors in its inheritance chain are called, in order, from most derived to least derived. Destructors are implemented by overriding the virtual method Finalize on System.Object. Programs are not permitted to override this method or call it (or overrides of it) directly. DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
47
8 Arrays
8.1 Introduction
An array is a data structure. It contains a number of variables, which are accessed through computed indices. Also called the elements of the array, the variables contained in an array are all of the same type, which is called the element type of the array. An array has a rank that determines the number of indices associated with each array element. The rank of an array is also referred to as the dimensions of the array. An array with a rank of one is called a single-dimensional array, while an array with a rank greater than one is called a multi-dimensional array. Multi-dimensional arrays of specific sizes are often referred to by size, as two-dimensional arrays, three-dimensional arrays, and so on. Each dimension of an array has an associated length that is an integral number greater than or equal to zero. The dimension lengths are not part of the type of the array; instead, they are established when an instance of the array type is created at run-time. The length of a dimension determines the valid range of indices for that dimension. For example, for a dimension of length N, indices can range from 0 to N 1 inclusive. The total number of elements in an array is the product of the lengths of each dimension in the array. If one or more of the dimensions of an array have a length of zero, the array is said to be empty. The element type of an array can be any type, including an array type.
48
creation expression, the array type immediately precedes the initializer. In a field or variable declaration, the array type is the type of the field or variable being declared. When an array initializer is used in a field or variable declaration, such as: int[] ar = {1, 3, 5, 7, 9}; it is simply shorthand for an equivalent array creation expression: int[] arr = new int[] {1, 3, 5, 7, 9} For a single-dimensional array, the array initializer must consist of a sequence of expressions that are assignment compatible with the element type of the array. The expressions initialize array elements in increasing order, starting with the element at index zero. The number of expressions in the array initializer determines the length of the array instance being created. For example, the array initializer above creates an int[] instance of length 5 and then initializes the instance with the following values: a[0] = 1; a[1] = 3; a[2] = 5; a[3] = 7; a[4] = 9; For a multi-dimensional array, the array initializer must have as many levels of nesting as there are dimensions in the array. The outermost nesting level corresponds to the leftmost dimension, and the innermost nesting level corresponds to the rightmost dimension. The length of each dimension of the array is determined by the number of elements at the corresponding nesting level in the array initializer. For each nested array initializer, the number of elements must be the same as the other array initializers at the same level. The example: int[,] ar = {{10, 11}, {12, 13}, {14, 15}, {16, 17}}; creates a two-dimensional array with a length of four for the leftmost dimension and a length of two for the rightmost dimension: int[,] ar = new int[4, 2]; and then initializes the array instance with the following values: ar[0, 0] = 0; b[0, 1] = 11; ar[1, 0] = 2; b[1, 1] = 13; ar[2, 0] = 4; b[2, 1] = 15; ar[3, 0] = 6; b[3, 1] = 17; ar[4, 0] = 8; b[4, 1] = 19; When an array creation expression includes both explicit dimension lengths and an array initializer, the lengths must be constant expressions and the number of elements at each nesting level must match the corresponding dimension length.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
49
9 Structures
9.1 Definition of a Struct
Structs are similar to classes in that they represent data structures that can contain data members and function members. Unlike classes, structs are value types and do not require heap allocation. A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to the data, the latter known as an object. Structs are particularly useful for small data structures that have value semantics. Complex numbers, points in a coordinate system, or key-value pairs in a dictionary are all good examples of structs. The simple types provided by C#, such as int, double, and bool, are in fact all struct types. It is possible to use structs and operator overloading to implement new primitive types in the C# language.
50
All struct types implicitly inherit from class object. A struct declaration may specify a list of implemented interfaces, but it is not possible for a struct declaration to specify a base class. Struct types are never abstract and are always implicitly sealed. The abstract and sealed modifiers are therefore not permitted in a struct declaration. Since inheritance is not supported for structs, the declared accessibility of a struct member cannot be protected or protected internal. Function members in a struct cannot be abstract or virtual, and the override modifier is allowed only to override methods inherited from the object type. Assignment to a variable of a struct type creates a copy of the value being assigned. This differs from assignment to a variable of a class type, which copies the reference but not the object identified by the reference. Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. A struct may be passed by reference to a function member using a ref or out parameter. When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. If the instance expression is classified as a value, a compile-time error occurs. A value of a class type can be converted to type object or to an interface type that is implemented by the class simply by treating the reference as another type at compile-time. Likewise, a value of type object or a value of an interface type can be converted back to a class type without changing the reference (but of course a run-time type check is required in this case). Since structs are not reference types, these operations are implemented differently for struct types. When a value of a struct type is converted to type object or to an interface type that is implemented by the struct, a boxing operation takes place. When a value of type object or a value of an interface type is converted back to a struct type, an unboxing operation takes place. A key difference from the same operations on class types is that boxing and unboxing copies the struct value either into or out of the boxed instance. Following a boxing or unboxing operation, changes made to the unboxed struct are not reflected in the boxed struct. Within an instance constructor or instance function member of a class, this is classified as a value. Thus, while this can be used to refer to the instance for which the function member was invoked, it is not possible to assign to this in a function member of a class. Within an instance constructor of a struct, this corresponds to an out parameter of the struct type, and within an instance function member of a struct, this corresponds to a ref parameter of the struct type. this is classified as a variable, and it is possible to modify the entire struct for which the function member was invoked by assigning to this or by passing this as a ref or out parameter. The default value of a struct consists of the value that results from setting all value type fields to their default value and all reference type fields to null. For this reason, a struct does not permit instance field declarations to include variable initializers. Unlike a class, a struct is not permitted to declare a parameterless instance constructor. Instead, every struct implicitly has a parameterless instance constructor that always returns the value that results from setting all value type fields to their default value and all reference type fields to null. A struct instance constructor is not permitted to include a constructor initializer of the form base(...). The this variable of a struct instance constructor corresponds to an out parameter of the struct type, and, similar to an out parameter, this must be definitely assigned at every location where the instance constructor returns. A struct can declare instance constructors having parameters. It is not permitted to declare a destructor.
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
51
10 Miscellaneous
10.1 C# 2.0
With the release of Visual Studio 2005, the C# language has been updated to version 2.0.
52
The List<T> Class The List<T> class implements the IList<T> interface using an array whose size is dynamically increased as required.
10.2 Generics
Generics are a new feature in version 2.0 of the C# language. Generic types are added to the language to enable the programmer to achieve a high level of code reuse and enhanced performance for collection classes. They are used with collections and the methods that operate on them. Generics introduce the concept of type parameters. Generic classes encapsulate operations that are not specific to any particular data type. The most common use for generic classes is with collections like: 1. 2. 3. 4. 5. Linked lists Hash tables Stacks Queues Trees
It is useful to define interfaces either for generic collection classes, or for the generic classes that represent items in the collection. It is preferable to use generic interfaces with generic classes. A generic method is a method that is declared with type parameters. Non-generic methods can access the class-level type parameters within a generic class. Generic methods can be overloaded on a number of type parameters. Delegates defined within a generic class can use the generic class type parameters in the same way that class methods do. Generic delegates are especially useful in defining events based on the typical design pattern. A new namespace called System.Collections.Generic includes several ready-to-use generic collection classes and associated interfaces.
10.4 DirectX
DirectX is a set of interfaces for creating games and other high-performance multimedia applications. It supports two-dimensional (2-D) and three-dimensional (3-D) graphics, sound effects and music, input devices, and networked applications.
10.4.1 Direct3D
Direct3D enables you to manipulate visual models of 3-dimensional objects and take advantage of hardware acceleration, such as video graphics cards. The RenderStateManager Class The RenderStateManager class defines device render states. The Texture Class DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: C# Programming Overview The Texture class manipulates a texture resource. The PresentParameters Class The PresentParameters class describes the presentation parameters.
53
The Device Class The Device class performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, gets and sets palettes, and creates shaders. The Sprite Class The Sprite class provides methods and properties that simplify the process of drawing sprites using Direct3D. The Font Class The Font class encapsulates the textures and resources needed to render a specific font on a specific device.
10.4.2 DirectInput
DirectInput is used to process data from a keyboard, mouse, joystick, or other game controller. The Device Class The Device class is used to gain and release access to DirectInput devices, manage device properties and information, set behavior, perform initialization, create and play force-feedback effects, and invoke a devices control panel. The Key Enumeration The Key enumeration includes all the available keyboard keys.
10.4.3 DirectSound
DirectSound is used to capture sounds from input devices and play sounds through various playback devices using advanced 3-dimensional positioning effects and filters for echo, distortion, reverberation, and other effects. The Device Class The Device class contains methods and properties that are used to create buffer objects, manage devices, and set up the environment. The SecondaryBuffer Class The SecondaryBuffer class contains methods and properties that are used to manage sound buffers that can support effects.
10.4.4 DirectX.AudioVideoPlayback
The AudioVideoPlayback interface provides for basic playback and simple control of audio and video files. The Audio Class The Audio class is primarily designed for very simple playback scenarios, or for use with the Video class.
54
DigiPen Game Development C# Webcast Series - Session Two, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Components
DigiPen Institute of Technology
5001 150th Ave NE, Redmond, WA 98052 Phone: (425) 558-0299 www.digipen.edu
Session Three:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Contents
1 Graphics
1.1 Two-Dimensional Graphics 1.2 Three-Dimensional Graphics 1.3 Bitmap-Based Graphics 1.4 Vector-Based Graphics 3 3 4 4
2 Input
2.1 Keyboards 2.2 Mouse 2.3 Joystick 2.4 Force Feedback with Input Devices 5 5 5 6
3 Sounds
3.1 Introduction 3.2 Uncompressed Audio Format 3.2.1 WAV File Format 3.2.2 MIDI (Musical Instrument Digital Interface) 3.3 Compressed Audio Format 3.3.1 MP3 3.3.2 OGG VORBIS 3.4 Three-Dimensional Audio 3.5 Interactive Game Audio 7 7 7 7 7 7 8 8 8
4 Networks
4.1 Introduction 4.2 Implementation of Multi-Player Games 4.2.1 Peer-to-Peer 4.2.2 Client-Server 4.2.2.1 Web Cam 4.2.2.2 Voice-Over IP (VoIP) 9 9 9 10 10 10
1 Graphics
1.1 Two-Dimensional Graphics
2D graphics are the computer-based generation of digital images mostly from two-dimensional models (2D geometric models, text, and digital images). These graphics are mainly used in applications that were originally developed using traditional printing and drawing techniques, such as typography, cartography, technical drafting, advertising, and so forth. In many domains, a description of a document based on 2D computer graphic techniques (vector-based), can be much smaller than the corresponding digital image often by a factor of 1/1000 or more. This representation is also more flexible since it can be rendered at different resolutions to suit specific output devices. 2D computer graphics started with vector-based graphic devices. In the following decades, bitmap-based devices largely supplanted these.
In 3D graphics, an object moves freely on the three axes. The orientation can be over the x-, y-, x y-, and z-axis. The rotation along the x-axis is called x pitch, along the y-axis yaw, and along the y z-axis roll.
For example, in a flight simulation, the plane can pitch, yaw, and roll.
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
2 Input
2.1 Keyboards
The keyboard is an input device with systematically arranged keys that allow users to type information, move the cursor, or activate functions assigned to keys. The keys on computer keyboards are often classified as follows: Alphanumeric keys: Letters and numbers to enter data. Punctuation keys: Comma, period, semicolon, and so on. Special keys: Function keys, control keys, arrow keys, Caps Lock key, and so on. In a game, the keyboard is used to enter text or data, to move objects, to access objects behaviors, to load, and to save the game.
2.2 Mouse
A mouse is a small object, which is rolled along a hard, flat surface. Mouse devices control the movement of the cursor or pointer on a display screen. As you move the mouse, the pointer on the display screen moves in the same direction. The mouse is used to move game objects and to shoot. The mouse frees users to a large extent from using the keyboard. In particular, the mouse is important for graphical user interfaces (GUI) because users can simply point to options and objects and click a mouse button to select that option. The mouse is also useful for graphics programs that allow users to draw images by using the mouse like a pen, pencil, or paintbrush.
2.3 Joystick
A joystick is a pointing device consisting of a hand-held stick that pivots about one end and transmits its angle in DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
two or three dimensions to a computer. Most joysticks are two-dimensional, having two axes of movement, just like a mouse, but three-dimensional joysticks do exist. Joysticks are often used to control games, and usually have one or more push-buttons whose state can also be read by the computer and often set by the user. An analog joystick is a joystick that has continuous states it returns an angle measure of the movement in any direction in the plane or the space. A digital joystick gives only on/off signals for four different directions and mechanically possible combinations (such as up-right, down-left, etc). Digital joysticks are very common as game controllers for video game consoles. Joysticks often have one or more fire buttons, used to trigger some kind of action which is visible on the screen. These are digital.
For example, a friction effect generates resistance to the movement of the joystick. The direction can be defined for the x-, y-, and z-axes. As with the joystick, the x-axis increases from left to right (- +), and the y-axis increases from far to near (- +). For example, an effect with a direction along the negative y-axis (-) tends to push the stick toward users along the y-axis. The magnitude is the strength of the force. It is measured in units starting from 0 (no force) until 10,000 (the maximum force). A negative value indicates that the force is in the opposite direction. The duration of an effect is measured in microseconds. Periodic effects have a period (the duration of one cycle), which is also measured in microseconds. The phase of a periodic effect is the point at which the playback begins.
A sawtooth periodic effect with a magnitude of 5,000, or half the maximum force for the device.
The Force Editor application offers the ability to design force-feedback effects and test them singly or in combination. Effects can be saved to file and then loaded into applications.
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
3 Sounds
3.1 Introduction
Sound and music always play a major role in games. Designers depend on sound and music to give players audio feedback, in addition to graphic feedback. Along with all the improvement in hardware technology, audio has also improved. Nowadays, various audio technologies exist, such as compressed, uncompressed, three-dimensional, and interactive audio. Game programmers often adjust between compressed and uncompressed audio formats, depending on the bottleneck in their data-loading pipeline. Game programmers change their decisions according to whether the load should be on the CPU (resources-decompressing audio at run time) or the hard-drive/CDROM/DVD where uncompressed file sizes can be10 to 20 times the size of a compressed file.
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
4 Networks
4.1 Introduction
Multi-player games are games played by two or more people on a network. Each player controls one or more game objects, and the effect of this action has to be replicated to all other players. Multi-player problems: Delay: The communication delay affects the game playability. Reliability: A major problem is the reliability of the communication. In any multi-player game, the main goal is to have a reliable transfer with a minimum amount of information required to maintain playability.
4.2.1 Peer-to-Peer
Peer-to-peer mode requires no server application to be developed. An application embedded in the game itself handles communication, and each player maintains his or her own copy of the game database. This solves the potential problem of a server bottleneck. If there are n players in the game, then a single change made by one player needs to be communicated to the other (n - 1) players. If all players make changesthe usual situation at any time during a gamethen n(n-1) messages need to be sent.
Player
Player
Player
Player
Player
Player
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
10
4.2.2 Client-Server
A client-server system is also known as a logical star configuration. In a client-server configuration, the server collects messages from all players. Clients, or players, only send messages to, or receive messages from, the server. Clients request from the server:
Player
To request to join an existing game or start a new one. To start the game and begin to play. To exit the session. Server response: The server collects messages from the clients and issues acknowledgements. On receipt of an acknowledgement, the client applies the command contained in the message.
Player
Player
Server
Player
Player
Player
In multi-player games, new technologies are adding excitement and amusement because players can see (using web cams) and talk (using voice-over IP) to each other during game play.
DigiPen Game Development C# Webcast Series - Session Three, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Session Four:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Contents
1 World Dimension
1.1 What Is a World? 1.2 Why Do We Use the World? 3 3
2 Viewport 3 Bitmap
3.1 What is a Bitmap? 3.2 RGB Intensity 3 3
4 Background
4.1 What Is a Background? 4.2 Position 3 3
5 Animations
5.1 What Is an Animation? 5.2 What Do We Do When the Animation Ends? 4 4
6 Frames
6.1 What Is a Frame? 6.2 Transparency 4 4
7 Sprite
7.1 What Is a Sprite? 7.2 Active 7.3 Visible 7.4 Animation 8 ZOrder 4 5 5 5 5
Game Implementation
Step 1: Adding the Background Step 2: Adding Sprites Step 3: Adding the Trooper Sprite Step 4: Adding the Condor Sprite Step 5: Adding Another Animation Explosion to the Condor Sprite 5 6 8 9 10
2 Viewport
A world rectangular coordinate selected for display is called a window. (Note: This is not the Operating System Window.) The window defines what is viewed, whereas the viewport defines where it is to be displayed. A viewport is a rectangular area of the display window. By default, it is the entire window (client area of the operating systems window) application. It can be set to any smaller size in pixels.
4.2 Position
The background should be positioned within the world. The backgrounds position is situated relatively to the world rectangle, not relatively to the viewport.
DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
6.2 Transparency
A frame is made out of a bitmap, which is a matrix of points that has a rectangular or square shape. Of course, not all images have a rectangular shape. Areas not used in the rectangle will be filled with one specific color called the transparency color. The drawing mechanism will ignore all pixels having the transparency color; in other words, the transparent pixels will not be drawn.
Sprites are the pictures displayed on top of the background. They are also called game objects. Usually, sprites represent all the moving parts of the game. Sprites can be: o o o o Enemies Animated parts of the background Bullets Any other moving game object
Since the sprite status varies in terms of images, position, and values, the sprite needs to save information such as: o o o o o o Current position Current speed Current direction Visibility status Current frame Current animation DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: Game Design Elements o Any other relevant information relating to image, position, or value
7.2 Active
This specifies whether the sprite will be active. When the sprite is not active, all its behaviors are disabled and it is not visible. Then the sprite will not be processed during the game loop. An inactive sprite is usually used when a sprite prototype is needed. A sprite prototype can be used to create identical sprites at run time. Inactive sprites are also used when a sprite does not need to be present when the level starts.
7.3 Visible
The code will process all the game objects with every game loop. Processing game objects includes handling and drawing. When the sprite is visible, it will be handled and drawn. However, when the sprite is invisible, it will be handled but not drawn.
7.4 Animation
The sprite or the game object requires an animation to be represented visually. During the process of the sprite creation, an animation with one or more frames should be specified.
8 ZOrder
To understand ZOrder, we have to simulate a 3D coordinates axis. Look at the diagram below and take note of the x-, y-, and z-axes. The z-axis specifies the depth axis, and the greater the value of the ZOrder, the deeper x y the object is to the screen. For example, the picture with the character has a lower ZOrder (1) while the picture with the ball has a higher ZOrder (2). Therefore, the ball appears to be behind the character. Because it is a 2D application, the ZOrder only affects the displacement order of pictures, without affecting their size. All the game objects are affected by the ZOrder.
DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: Game Design Elements Add the sprite to the game. Game.Add(bg); Create the second background object bg2 that is derived from the Sprite class. Background bg2 = new Background(); Add the animation object already created. bg2.Add(backGroundAnimation); Set the sprite position. bg2.Position = new Point(320, 240); Set the sprite Size to fit the screen. bg2.ScaleX = 640.0f / background.Width; bg2.ScaleY = 480.0f / background.Height; Set the background order to 10. bg2.ZOrder = 10; Add the sprite to the game. Game.Add(bg2);
Note: The path of the directory holding the pictures as BMP files, should be specified in the constructor of the StarTrooperGame class. PicturesPath = Application.StartupPath+\\StarTrooperResources\\Pictures\\; Type the following code in the StarTrooperGame.cs file in order to add the background to the game.
Picture background = new Picture("Background.bmp", Color.FromArgb(0 , 255, 0)); Game.Add(background); Frame backGroundFrame = new Frame(background, 0); Animation backGroundAnimation = new Animation(); backGroundAnimation.Add(backGroundFrame); Background bg = new Background(); bg.Add(backGroundAnimation); bg.Position = new Point(320, 240); bg.ScaleX = 640.0f / background.Width; bg.ScaleY = 480.0f / background.Height; bg.ZOrder = 10; Game.Add(bg); Background bg2 = new Background(); bg2.Add(backGroundAnimation); bg2.Position = new Point(320, -240); bg2.ScaleX = 640.0f / background.Width; bg2.ScaleY = 480.0f / background.Height; bg2.ZOrder = 10; Game.Add(bg2);
DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game.Add(trooper); Trooper = trooper; Note: The path of the directory holding the pictures as BMP files, should be specified in the constructor of the StarTrooperGame class. PicturesPath = Application.StartupPath+\\StarTrooperResources\\Pictures\\; Type the following code in the StarTrooperGame.cs file in order to add two sprites to the game.
Picture trooper01 = new Game.Add(trooper01); Picture trooper02 = new Game.Add(trooper02); Picture trooper03 = new Game.Add(trooper03); Picture trooper04 = new Game.Add(trooper04); Picture trooper05 = new Game.Add(trooper05); Picture trooper06 = new Game.Add(trooper06); Frame Frame Frame Frame Frame Frame afTrooper01 afTrooper02 afTrooper03 afTrooper04 afTrooper05 afTrooper06 = = = = = = new new new new new new Picture("trooper01.bmp", Color.FromArgb(0 , 255, 0)); Picture("trooper02.bmp", Color.FromArgb(0, 255, 0)); Picture("trooper03.bmp", Color.FromArgb(0, 255, 0)); Picture("trooper04.bmp", Color.FromArgb(0, 255, 0)); Picture("trooper05.bmp", Color.FromArgb(0, 255, 0)); Picture("trooper06.bmp", Color.FromArgb(0, 255, 0)); Frame(trooper01, Frame(trooper02, Frame(trooper03, Frame(trooper04, Frame(trooper05, Frame(trooper06, 5); 5); 5); 5); 5); 5);
Animation trooperAnimation = new Animation(); trooperAnimation.Add(afTrooper01); trooperAnimation.Add(afTrooper02); trooperAnimation.Add(afTrooper03); trooperAnimation.Add(afTrooper04); trooperAnimation.Add(afTrooper05); trooperAnimation.Add(afTrooper06); trooperAnimation.Play(); trooperAnimation.Loop = true; Trooper trooper = new Trooper(); trooper.Add(trooperAnimation); trooper.Position = new Point(320, 450); Game.Add(trooper); Trooper = trooper;
Each picture is a class Picture object that holds the bitmap. Picture condor01 = new Picture(condor01.bmp, Color.FromArgb(0, 255, 0)); Where: The first argument is the name of the bitmap. The second argument is the transparency color. The Picture object is added to the game in order to be used by the frame. Game.Add(condor01); The frame is a class Frame object that uses the picture object. DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: Game Design Elements Frame afcondor01 = new Frame(condor01, 5); Where:The first argument is a picture object. The second argument is the frame delay. The animation is a class Animation object that is composed of many frames. Animation condorAnimation = new Animation(); Each Frame is added to the animation object. condorAnimation.Add(afcondor01); Start playing the animation object. condorAnimation.Play(); Set the loop property of the animation object to be true in order to loop continuously. condorAnimation.Loop = true;
10
11
Add the animation to the object already created. condor.Add(condorAnimation); Add the explosion animation to condor sprite. condor.Add(condorExplosion); Add the sprite to the game. Condor = condor; Type the following code in the StarTrooperGame.cs file in order to add two sprites to the game.
Picture condor01 = new Game.Add(condor01); Picture condor02 = new Game.Add(condor02); Picture condor03 = new Game.Add(condor03); Picture condor04 = new Game.Add(condor04); Frame Frame Frame Frame afcondor01 afcondor02 afcondor03 afcondor04 = = = = new new new new Picture("condor01.bmp", Color.FromArgb(0, 255, 0)); Picture("condor02.bmp", Color.FromArgb(0, 255, 0)); Picture("condor03.bmp", Color.FromArgb(0, 255, 0)); Picture("condor04.bmp", Color.FromArgb(0, 255, 0)); Frame(condor01, Frame(condor02, Frame(condor03, Frame(condor04, 5); 5); 5); 5);
Animation condorAnimation = new Animation(); condorAnimation.Add(afcondor01); condorAnimation.Add(afcondor02); condorAnimation.Add(afcondor03); condorAnimation.Add(afcondor04); condorAnimation.Play(); condorAnimation.Loop = true; Picture condorExplosion01 = new Picture("condorExplosion01.bmp", Color.FromArgb(0, 255, 0)); Game.Add(condorExplosion01); Picture condorExplosion02 = new Picture("condorExplosion02.bmp", Color.FromArgb(0, 255, 0)); Game.Add(condorExplosion02); Picture condorExplosion03 = new Picture("condorExplosion03.bmp", Color.FromArgb(0, 255, 0)); Game.Add(condorExplosion03); Frame afcondorExplosion01 = new Frame(condorExplosion01, 4); Frame afcondorExplosion02 = new Frame(condorExplosion02, 3); Frame afcondorExplosion03 = new Frame(condorExplosion03, 4); Animation condorExplosion = new Animation(); condorExplosion.Add(afcondorExplosion01); condorExplosion.Add(afcondorExplosion02); condorExplosion.Add(afcondorExplosion03); condorExplosion.Play(); Condor condor = new Condor(); condor.Add(condorAnimation); condor.Add(condorExplosion); Condor = condor;
DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
12
Note: In this game, by default, sprite classes are created for you in order to only create an object from them (Trooper, Condor, and Fire (later)). Therefore, in order to add a new sprite class (for example, a car) add a file called car.cs and create a class called car that is derived from the Sprite class.
#region Using directives using using using using using System; System.Collections.Generic; System.Drawing; Microsoft.DirectX.DirectInput; Microsoft.DirectX;
#endregion using System.Windows.Forms; namespace StarTrooper { public class Car: Sprite { public Car() { } protected Car(Car car): base(car) { } public override Object Clone() { return new Car(this); } public override void Update() { } } }
Note: Using our game engine, you can even create a new game (totally different storyline) by adding a file called yourgame.cs in which you create a class called yourgame derived from the Game class.
DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
13
#region Using directives using using using using System; System.Collections.Generic; System.Drawing; System.Windows.Forms;
#endregion namespace StarTrooper { public class YourGame: Game { public YourGame () { } public override void InitializeResources() { } public override void Update() { } } }
#region Using directives using System; using System.Windows.Forms; #endregion namespace StarTrooper { static class Program { [STAThread] static void Main() { using (YourGame yourGame = new YourGame()) yourGame.Run(); } } }
DigiPen Game Development C# Webcast Series - Session Four, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Session Five:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Contents
1 Transformation
1.1 Translations 1.2 Rotations 1.3 Scaling 3 3 4
2 Rectangular Collision
2.1 What Is a Rectangular Collision? 2.2 Detecting Collision or Intersection 2.3 Different Coordinate Systems 4 4 5
3 Velocity
3.1 Direction 3.2 Sprite Direction Using Vector Coordinates (x, y) 3.3 Speed 6 6 7
Facts: tx and ty are called translation distances along the x-axis and the y-axis. (tx, ty) is called the translation vector.
1.2 Rotations
Rotations rotate a point along a circular path. To specify a rotation transformation we need: An angle. A pivot point (reference for the rotation). A rotation axis. In 2D, the axis is perpendicular to the x-y plane. It is the z-axis. To specify a positive (counterclockwise) and a negative (clockwise) rotation angles.
DigiPen Game Development C# Webcast Series - Session Five, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
1.3 Scaling
Scaling alters the size of objects. It requires scaling factors sx and sy that determine the change in the x-direction x and the y-direction. Scaling is carried out by multiplying the coordinate values of each vertex of object with the y scaling factors. Example: x = sx x y = sy y If sx sy, we have a uniform scaling; if sx sy, we have a non-uniform scaling.
Facts: The limit of the first bounding box is determined by: L = 1, R = 3, T = 3, and B = 1. The limit of the second bounding box is determined by: L = 2, R = 4, T = 4, and B = 2. Find the following: o Max(L, L) = Max(1, 2) = 2 o Min(R, R) = Min(3, 4) = 3 o Max(B, B) = Max(1, 2) = 2 o Min(T, T) = Min(3, 4) = 3 The two bounding boxes intersect if: o Max(Max(L,L) Min(R, R), Max(B, B) Min(T, T) )<=0. DigiPen Game Development C# Webcast Series - Session Five, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Game Web Cast Project: Transformations, Collisions & Velocity o Max(2 3, 2 - 3) )<=0 => Max(-1, -1) = -1 Result: The two bounding boxes intersect and form a new rectangle: L=2, R=3, T=3, and B=2.
Facts: The limit of the first bounding box is determined by: L = 1.2, R = 2.8, T = 2.6, and B = 1. The limit of the second bounding box is determined by: L = 3.6, R = 5.3, T = 4.5, and B = 3. Find the following: o Max(L, L) = Max(1.2, 3.6) = 3.6 o Min(R, R) = Min(2.8, 5.3) = 2.8 o Max(B, B) = Max(1, 3) = 3 o Min(T, T) = Min(2.6, 4.5) = 2.6 The two bounding boxes intersect if: o Max(Max(L,L) Min(R, R), Max(B, B) Min(T, T) )<=0. o Max(3.6 2.8, 3 2.6) )<=0 => Max(0.8, 0.4) = 0.8 >0 Result: The two bounding boxes do not intersect since the result is positive.
DigiPen Game Development C# Webcast Series - Session Five, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
The same equation of the previous section applies with the following modifications: Nothing changes regarding the left and the right. Since the top has a value less than the bottom: o Max(B, B) will be Min(B, B), and o Min(T, T) will be Max(T, T). Finally, the equation should be: o Max(Max(L,L) Min(R, R), Min(B, B) Max(T, T) )<=0.
3 Velocity
The velocity V(x, y) is the speed and direction describing a moving object.
3.1 Direction
Unit vectors are used to indicate the direction. Example: uv (x/l , y/l) where l = (x2 + y2)
Game Web Cast Project: Transformations, Collisions & Velocity Get the length L of the vector (x, y) L = (x2 + y2) The unit vector will be: X=x/L Y=y/L
x
According to a different coordinate system, X=x/L Y = -y / L y the unit vector will be:
y X = 0, Y = -1 Upward X = 0, Y = 1 Downward
X = 1, Y = 0 Forward
X = -1, Y = 0 Backward
3.3 Speed
The speed is the magnitude of V(x, y). Example: S = (x2 + y2)
Game Implementation: Adding movement to the Condor sprite Step 1: Positioning the Condor Sprite
Create a copy of the Condor object with all its properties and values and called it condor. Condor condor = (Condor)Condor.Clone(); Set the sprite position. condor.Position = new Point(m_Random.Next(-100, 740), -150); Add the sprite to the game. Game.Add(condor); Type the following code in the StarTrooperGame.cs file in the StarTrooper class under public override void Update() function inside the if (){ } statement.
DigiPen Game Development C# Webcast Series - Session Five, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Session Six:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Contents
1 Dynamic Sprite Basics
1.1 How To Create a Sprite Dynamically 1.2 When Are Dynamic Sprites Used? 3 3
2 Input
2.1 Keyboard 2.2 Pressed 2.3 Triggered 4 4 4
Game Implementation
Step 1: Create a Prototype Step 2: Enter Input Information 4 5
Note: The sprite fire is not added to the game, so it is not active. After creating a prototype, create a copy of it. Then set the copy properties like position, velocity, etc. To create a copy of the prototype, type the following: Fire fire = (Fire)StarTrooperGame.Fire.Clone(); fire.Position = new PointF(Position.X, Position.Y 35); fire.Velocity = new Vector2(0, -4); // up direction, speed=4 Game.Add(fire);
Dynamic sprites are used when we need to create sprites and use them during the game play. For example, dynamic sprites are used to create bullets to shoot or a new life. DigiPen Game Development C# Webcast Series - Session Six, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
2.2 Pressed
The action assigned to the keyboard input will be executed as long as the key is pressed. To get the status of a key (if it is pressed or not), call the IsPressed() function. Example: Keyboard.IsPressed(Key.UpArrow) This function returns a Boolean value even TRUE (1) or FALSE (0).
2.3 Triggered
The action assigned to the keyboard input will be executed when the key status changes from not pressed to pressed. To get the status of a key (if it is triggered or not), call the IsTriggered() function. Example: Keyboard.IsTriggered(Key.Space) This function returns a Boolean value even TRUE (1) or FALSE (0).
Game Implementation
In the game, the arrow keys are used to move the main sprite (trooper sprite). Pressing the Up arrow moves the trooper upward. Pressing the Down arrow moves the trooper downward. Pressing the Left arrow moves the trooper to the left. Pressing the Right arrow moves the trooper to the right. Also, when the Space key is triggered, then the main sprite (trooper) will shoot a bullet (fire sprite), which is a sprite created dynamically.
DigiPen Game Development C# Webcast Series - Session Six, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
= 0; > 50 && Keyboard.IsPressed(Key.UpArrow)) // if trooper is under y=50 then go upward < 450 && Keyboard.IsPressed(Key.DownArrow)) // if trooper is over y=450 then go upward > 30 && Keyboard.IsPressed(Key.LeftArrow)) -1; // go to the left // left flip trooper
< 610 && Keyboard.IsPressed(Key.RightArrow)) 1; // go to the right // right flip trooper // set new velocity for Trooper
// if space bar is triggered if (Keyboard.IsTriggered(Key.Space)) { // dynamically create a new sprite Fire fire = (Fire)StarTrooper.Fire.Clone(); fire.Position = new PointF(Position.X, Position.Y - 35); fire.Velocity = new Vector2(0, -4); Game.Add(fire); // set the fire sprite active }
DigiPen Game Development C# Webcast Series - Session Six, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Session Seven:
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
1 Sound
Contents
3 3 3 3
1.1 Sound Effects 1.2 Game Implementation: Adding Sound Effects 1.3 Music 1.4 Game Implementation: Adding Music
2 Text
2.1 What Is Text? 2.2 Why Do We Use It? 2.3 Game Implementation: Adding Text 4 4 4
1.3 Music
The music is the sound that is played on the background while the game is being played. It can be repetitive or just played once. Music files can be midi, wave, or compressed.
Music = new Music(music.wav); The class Music object is then added to the game. Game.Add(Music); Since the music is played continuously in the background, start playing it. Music.Play(); Note: The path of the directory holding the music MIDI files should be specified in the constructor of the StarTrooperGame class. MusicPath = Application.StartupPath + \\StarTrooperResources\\Music\\; Type the following code in the StarTrooperGame.cs file in order to add and start playing the music:
To play the music continuously, add the following code in the StarTrooperGame.cs file under public override void Update() function:
if (!Music.IsPlaying) Music.Play();
Font font = new Font(Arial, 14.0f, FontStyle.Regular); Where: The first parameter is the font type. The second parameter is the font size. The third parameter is the font style. Add the created Font object to the game in order to be used by a text. Game.Add(font); Then, create the text to display. The text is a class Text2D object that holds the text displayed on the screen. public static Text2D Score; Text2D Score = new Text2D(font); Specify the text value, position, and color. Score.Text = Score: 0; Score.Position = new Point(150, 0); Score.Color = Color.Red; Specify the text to act like a number. StarTrooperGame.Score = Score; (Skip this step in order to display a string text) Finally, the class Text2D object is then added to the game in order to be seen. Game.Add(Score); Type the following code in the StarTrooperGame.cs file in order to display the score and number of shot bullets (Shoots) texts:
DigiPen Game Development C# Webcast Series - Session Seven, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Font font = new Font(Arial, 14.0f, FontStyle.Regular); Game.Add(font); Text2D Shoots = new Text2D(font); Shoots.Text = Shoots: 0; Shoots.Position = new Point(0, 0); Shoots.Color = Color.Green; StarTrooper. Shoots = Shoots; Game.Add(Shoots); Text2D Score = new Text2D(font); Score.Text = Score: 0; Score.Position = new Point(150, 0); Score.Color = Color.Red; StarTrooper.Score = Score; Game.Add(Score);
Optional: To display the Frame rate on the screen, first set the Frame rate value in the constructor of the StarTrooperGame class. FrameRate = 60; Then type the following code in the StarTrooperGame.cs file under public override void InitializeResources() function:
Note: In order to add a new text class, add a file called yourText.cs and create a class called YourText, which is derived from the Text2D class.
#region Using directives using using using using System; System.Collections.Generic; System.Drawing; Microsoft.DirectX.DirectInput;
#endregion namespace StarTrooper { class FrameRate : Text2D { public FrameRate(Font font): base(font) { } public override void Update() { } } }
DigiPen Game Development C# Webcast Series - Session Seven, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Session Eight:
Behavior
2005 DigiPen (USA) Corporation. No part of this work may be published without the written permission of DigiPen (USA) Corporation
Contents
1 Behavior Overview 2 Game Objects Behavior
2.1 Game Implementation: Condor Behavior 2.2 Game Implementation: Fire Behavior 3 5
Behavior
1 Behavior Overview
A behavior is a set of functions, defined by the user, which describes how a game and its objects should behave and act. You can define a behavior to the game and to some of its objects, like sprites and texts. The functions assigned in a game object behavior (the game object is an instance of a class derived from the Game class) are executed successively.
Game Web Cast Project: Behavior m_Score++; //Updating the value of the score displayed //on the screen RaptorGame.Score.Text = Score: + m_Score.ToString(); //Removing the enemy sprite from the game Game.Remove(s); break; } o Test if this element is the main character: else if (s is Bat) { m_CollisionWithBat = true; //The enemy object will die RaptorGame.Die.Play(); //The Animation will stop looping Animation.Stop(); //Decrementing the score by one m_Score--; //Updating the value of the score displayed //on the screen RaptorGame.Score.Text = Score: + m_Score.ToString(); break; } Testing if the animation of the Condor sprite is the explosion animation or not: if (AnimationIndex != 1) { } else { //Testing if it is the last frame of the explosion animation if (Animation.PlayingLastFrame) //delete the explosion sprite Game.Remove(this); } Delete the following code in the StarTrooperSprites.cs file under public override void Update() function:
Trooper b = StarTooper.Trooper; Vector2 v = new Vector2(b.Position.X - Position.X, b.Position.Y - Position.Y); v.Normalize(); Velocity = v; if (v.X >= 0) ScaleX = 1; else ScaleX = -1;
Then type the following code in the StarTrooperSprites.cs file under public override void Update() function:
DigiPen Game Development C# Webcast Series - Session Eight, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
Trooper b = StarTrooper.Trooper; if (AnimationIndex != 1) { Vector2 v = new Vector2(b.Position.X - Position.X, b.Position.Y - Position.Y); v.Normalize(); Velocity = v; if (v.X >= 0) ScaleX = 1; else ScaleX = -1; List<Sprite> collidedSprites = Game.GetCollidedSprites(this); if (collidedSprites != null) { foreach (Sprite s in collidedSprites) { if (s is Fire) { StarTrooper.Die.Play(); AnimationIndex = 1; m_Score++; StarTrooper.Score.Text = "Score: " + m_Score.ToString(); Game.Remove(s); break; } else if(s is Trooper) { m_CollisionWithTrooper = true; StarTrooper.Die.Play(); Animation.Stop(); m_Score--; StarTrooper.Score.Text = "Score: " + m_Score.ToString(); break; } } } } else { if (Animation.PlayingLastFrame) Game.Remove(this); }
When the spacebar is triggered, a new Fire sprite is created, the Shoot sound effect is played, and the text Shoots is incremented and displayed. Delete the following code in the StarTrooperSprites.cs file under public override void Update() function:
DigiPen Game Development C# Webcast Series - Session Eight, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.
if (Keyboard.IsTriggered(Key.Space)) { Fire fire = (Fire)StarTrooper.Fire.Clone(); fire.Position = new PointF(Position.X, Position.Y - 35); fire.Velocity = new Vector2(0, -4); Game.Add(fire); }
Then type the following code in the StarTrooperSprites.cs file under public override void Update() function:
if (Keyboard.IsTriggered(Key.Space)) { Fire fire = (Fire)StarTrooper.Fire.Clone(); fire.Position = new PointF(Position.X, Position.Y - 35); fire.Velocity = new Vector2(0, -4); Game.Add(fire); StarTrooper.Shoot.Play(); m_Shoots++; StarTrooper.Shoots.Text = "Shoots:" + m_Shoots.ToString(); }
DigiPen Game Development C# Webcast Series - Session Eight, Version 1.0 Copyright 2005 DigiPen (USA) Corporation.