main.cs
来自「Beginning C# Game Programming 的源代码」· CS 代码 · 共 438 行
CS
438 行
using System;
using System.Drawing;
using System.Threading;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectInput;
using DI = Microsoft.DirectX.DirectInput;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectSound;
using DS = Microsoft.DirectX.DirectSound;
using Container = System.ComponentModel.Container;
using System.Windows.Forms;
/// <summary>
/// The main windows form for the application.
/// </summary>
namespace SpaceDonuts {
public class MainClass : System.Windows.Forms.Form{
private int screenWidth = 800;
private int screenHeight = 600;
private String TileSetFileName = "donuts.bmp";
private TileSet donutTileSet;
private TileSet pyramidTileSet;
private TileSet cubeTileSet;
private TileSet sphereTileSet;
private TileSet shipTileSet;
private TileSet nixiesTileSet;
private TileSet bulletTileSet;
private Texture donutTexture;
private BasicSprite s; //temp holder for sprite creation
private ShipSprite ship; //reference to our ship
private D3D.Device device;
private DI.Device kbd;
internal HighResolutionTimer hrt = new HighResolutionTimer();
private SpriteManager sm;
private System.ComponentModel.Container components = null;
private float deltaTime;
//Bullet state data
private float lastBullet;
private const float bulletSpacing = 0.01f;
private const double bulletFireRadius = 22.6f;
private Random rnd = new Random(); //no fancy seeding
//Scorekeeping data
private int totalScore = 0;
private int livesRemaining = 2;
private int gameLevel = 0;
private int totalTargets = 0;
private const int maxScoreDigits = 8;
private const int maxLevelDigits = 3;
private Sounds gameSounds;
private Sounds shipSounds;
private SoundHandler soundHandler;
public MainClass() {
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
this.Text = "Space Donuts";
sm = new SpriteManager();
}
protected override void Dispose( bool disposing ) {
if( disposing ) {
if (components != null) {
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
//
// MainClass
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(640, 480);
this.Name = "MainClass";
this.Text = "Form1";
}
#endregion
[STAThread]
public static void Main() {
using (MainClass frm = new MainClass()) {
frm.Show();
frm.Initialize();
Application.Run(frm); //triggers OnPaint event, which is main loop
}
Application.Exit();
}
private void CollisionHandler(BasicSprite s1, BasicSprite s2) {
//Check to see if a bullet or ship is hitting a target object
BasicSprite collidable;
BasicSprite target;
if ((s1.GetType() == typeof(ShipSprite)) || (s1.GetType() == typeof(BulletSprite))) {
collidable = s1;
target = s2;
}
else {
collidable = s2;
target = s1;
}
//remove the bullet/ship from collision checking and take off list
collidable.Visible = false; //will be ignored for future collisions
collidable.DurationOver = true; //will be removed at next update
//if it was a ship, take away a life and restart the ship
if (collidable.GetType() == typeof(ShipSprite)) {
shipSounds = Sounds.ShipExplode;
ship.Visible = false;
//remove the ship from the sprite manager
ship.DurationOver = true;
//subtract a life
livesRemaining--;
//now make a new ship
NewShip();
}
//Blow up object
Destroy(target);
}
private void Destroy(BasicSprite sprite) {
totalTargets--;
gameSounds = 0; //clear out game sounds
//sprite type should be: donut, pyramid, cube, or sphere
if (sprite.GetType() == typeof(DonutSprite)) {
for(int i = 0; i < 3; i++) {
s = new PyramidSprite(pyramidTileSet);
s.PositionY = sprite.PositionY;
s.PositionX = sprite.PositionX;
s.Angle = (float)rnd.Next(0, 359);
s.Velocity = (float) rnd.Next(30, 200);
sm.AddSprite(s);
totalTargets++;
gameSounds |= Sounds.DonutExplode;
}
totalScore += 10;
}
else if (sprite.GetType() == typeof(PyramidSprite)) {
for(int i = 0; i < 2; i++) {
s = new CubeSprite(cubeTileSet);
s.PositionY = sprite.PositionY;
s.PositionX = sprite.PositionX;
s.Angle = (float)rnd.Next(0, 359);
s.Velocity = (float) rnd.Next(30, 200);
s.Visible = true;
sm.AddSprite(s);
totalTargets++;
gameSounds |= Sounds.PyramidExplode;
}
totalScore += 20;
}
else if (sprite.GetType() == typeof(CubeSprite)) {
for(int i = 0; i < 2; i++) {
s = new SphereSprite(sphereTileSet);
s.PositionY = sprite.PositionY;
s.PositionX = sprite.PositionX;
s.Angle = (float)rnd.Next(0, 359);
s.Velocity = (float) rnd.Next(50, 200);
sm.AddSprite(s);
totalTargets++;
gameSounds |= Sounds.CubeExplode;
}
totalScore += 10;
}
else if (sprite.GetType() == typeof(SphereSprite)) {
totalScore += 10;
gameSounds |= Sounds.SphereExplode;
}
sprite.Visible = false; //will be ignored for future collisions
sprite.DurationOver = true; //will be removed at next update
}
private void Initialize() {
try {
//Common DirectX setup calls...
PresentParameters presentParams = new PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferFormat = Format.Unknown;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;
presentParams.EnableAutoDepthStencil = true;
// Store the default adapter
int adapterOrdinal = D3D.Manager.Adapters.Default.Adapter;
CreateFlags flags = CreateFlags.SoftwareVertexProcessing;
// Check to see if we can use a pure hardware device
D3D.Caps caps = D3D.Manager.GetDeviceCaps(adapterOrdinal, D3D.DeviceType.Hardware);
// Do we support hardware vertex processing?
if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
// Replace the software vertex processing
flags = CreateFlags.HardwareVertexProcessing;
device = new D3D.Device(0, D3D.DeviceType.Hardware, this, flags, presentParams);
device.DeviceReset += new System.EventHandler(this.OnResetDevice);
OnResetDevice(device, null);
//Space Donuts setup
donutTexture = TextureLoader.FromFile(device, MediaUtilities.FindFile(TileSetFileName), 1024, 1024,
1, 0,Format.A8R8G8B8, Pool.Managed, Filter.Point, Filter.Point, (unchecked((int)0xff000000)));
donutTileSet = new TileSet(donutTexture, 0, 0, 6, 5, 32, 32);
pyramidTileSet = new TileSet(donutTexture, 0, 384, 4, 10, 16, 16);
sphereTileSet = new TileSet(donutTexture, 0, 512, 2, 20, 8, 8);
cubeTileSet = new TileSet(donutTexture, 0, 544, 2, 20, 8, 8);
shipTileSet = new TileSet(donutTexture, 0, 576, 4, 10, 16, 16);
nixiesTileSet = new TileSet(donutTexture, 0, 832, 1, 14, 8, 8);
bulletTileSet = new TileSet(donutTexture, 304, 832, 1, 1, 8, 2);
//set up DirectInput keyboard device...
kbd = new DI.Device(SystemGuid.Keyboard);
kbd.SetCooperativeLevel(this,
DI.CooperativeLevelFlags.Background | DI.CooperativeLevelFlags.NonExclusive);
kbd.Acquire();
soundHandler = new SoundHandler(this);
sm.OnCollisionDetected += new SpriteManager.HandleCollision(this.CollisionHandler);
hrt.Start();
}
catch (DirectXException e) {
Console.WriteLine("Exception is " + e.ErrorString);
// Catch any errors and return a failure
}
} private void NewShip() { ship = new ShipSprite(shipTileSet); ship.CollisionxExtent = 8; //make collision box smaller ship.CollisionyExtent = 8; ship.PositionY = (float)this.Height/2; ship.PositionX = (float)this.Width/2; ship.Velocity = 0f; ship.CanCollide = true; ship.Angle = 0; ship.StartDelay = 2f; //delay start for 2 seconds ship.AnimationSpeed = 0f; //ship only moves from user input ship.Frame = 10; //aligns ship direction to 0 radians sm.AddSprite(ship); shipSounds = Sounds.ShipAppear | Sounds.ShipHum; }
private void NewLevel() {
gameLevel++;
//Reset game sounds
shipSounds = Sounds.ShipHum;
gameSounds = Sounds.LevelStart;
soundHandler.Play(shipSounds | gameSounds);
DisplayLevel(gameLevel);
//Remove all sprites from the sprite manager's list
sm.Clear();
//Create new entities
NewShip();
for(int i = 0; i < gameLevel; i++) {
s = new DonutSprite(donutTileSet);
s.CollisionxExtent = 24; //make collision box smaller
s.CollisionyExtent = 24;
s.PositionY = rnd.Next(donutTileSet.ExtentY*4, this.Height-donutTileSet.ExtentY*4);
s.PositionX = rnd.Next(donutTileSet.ExtentX*4, this.Width-donutTileSet.ExtentX*4);
s.Angle = (float)rnd.Next(10, 350);
s.Velocity = (float) rnd.Next(75, 150);
s.CanCollide = false;
totalTargets++;
sm.AddSprite(s);
}
shipSounds = 0;
gameSounds = 0;
hrt.Reset();
}
public void DisplayLevel(int level) {
int characterStart = 30;
int characterSpacing = 15;
Vector3 displayPosition =
new Vector3((float)this.Width/2 - characterStart, (float)this.Height/2, 0f);
int digit;
NixieSprite nixie = new NixieSprite(nixiesTileSet);
//Render the level indicator
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
device.BeginScene();
using (D3D.Sprite d3dSprite = new D3D.Sprite(device)) {
d3dSprite.Begin(D3D.SpriteFlags.AlphaBlend);
//Show the letter L
nixie.Draw(d3dSprite, NixieSprite.NixieCharacters.L, displayPosition);
//Show the letter E
displayPosition.X -= characterSpacing;
nixie.Draw(d3dSprite, NixieSprite.NixieCharacters.E, displayPosition);
//Show the letter V
displayPosition.X -= characterSpacing;
nixie.Draw(d3dSprite, NixieSprite.NixieCharacters.V, displayPosition);
//Show the letter E
displayPosition.X -= characterSpacing;
nixie.Draw(d3dSprite, NixieSprite.NixieCharacters.E, displayPosition);
//Show the letter L
displayPosition.X -= characterSpacing;
nixie.Draw(d3dSprite, NixieSprite.NixieCharacters.L, displayPosition);
displayPosition.X = (float)this.Width/2 + 40;
for (int digitCount = 1; digitCount <= maxLevelDigits; digitCount++) {
digit = level % 10;
level /= 10;
nixie.Draw(d3dSprite, (NixieSprite.NixieCharacters)digit, displayPosition);
displayPosition.X -= characterSpacing;
}
d3dSprite.End();
}
device.EndScene();
device.Present();
Thread.Sleep(3000); //wait for 3 seconds
}
public void OnResetDevice(object sender, EventArgs e) {
D3D.Device device = (D3D.Device)sender;
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
//Update game state
if (totalTargets == 0) NewLevel();
if ((gameSounds|shipSounds) != 0) {
soundHandler.Play(gameSounds | shipSounds);
shipSounds = Sounds.ShipHum;
gameSounds = 0;
}
deltaTime = hrt.ElapsedTime;
ProcessInputState(deltaTime); //get keyboard input
sm.Update(deltaTime);
sm.CollisionTest();
//Render the images
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue, 1.0f, 0);
device.BeginScene();
sm.Draw(device);
WriteScore(device, totalScore);
device.EndScene();
device.Present();
this.Invalidate();
}
protected void ProcessInputState(float delta) {
foreach (Key k in kbd.GetPressedKeys()) {
if (k == Key.Space && ship.Visible) {
//Fire guns
lastBullet += delta;
if (lastBullet > bulletSpacing) {
BulletSprite bullet = new BulletSprite(bulletTileSet);
//Calculate bullet start position, outside ship boundaries
float radAngle = Geometry.DegreeToRadian(ship.Angle);
int yOffset = (int) ( bulletFireRadius * Math.Sin((double)radAngle));
int xOffset = (int) ( bulletFireRadius * Math.Cos((double)radAngle));
bullet.PositionY = (ship.PositionY) + shipTileSet.ExtentY + yOffset;
//the -4 below is a small nudge to center up the bullets
bullet.PositionX = (ship.PositionX) + shipTileSet.ExtentX + xOffset - 4;
bullet.Angle = ship.Angle;
bullet.Velocity = 150f;
bullet.AnimationSpeed = 0f;
bullet.CanCollide = true;
bullet.LimitLifespan(2f); //only 2 seconds to live
sm.AddSprite(bullet);
lastBullet = 0f;
if (totalScore > 0) totalScore--; //lose a point for each bullet
shipSounds |= Sounds.ShipFire;
}
}
if (k == Key.Left) ship.PrevFrame();
if (k == Key.Right) ship.NextFrame();
if (k == Key.Up) {
ship.Thrust();
shipSounds |= Sounds.ShipThrust;
}
if (k == Key.Down) {
//Put on the brakes!
ship.VelocityX = 0f;
ship.VelocityY = 0f;
shipSounds |= Sounds.ShipBrake;
}
if (k == Key.Escape) {
kbd.Unacquire(); //release the keyboard device
kbd.Dispose();
Application.Exit();
}
if (k == Key.Home) {
//resets ship to starting position
ship.PositionY = (float)this.Height/2;
ship.PositionX = (float)this.Width/2;
ship.VelocityX = 0f;
ship.VelocityY = 0f;
ship.Angle = 0.0f;
ship.Frame = 10;
}
//if (k == Key.D) sm.ShowList();
}
}
public void WriteScore(D3D.Device device, int score) {
Rectangle nixiePosition = new Rectangle(nixiesTileSet.XOrigin,nixiesTileSet.YOrigin,nixiesTileSet.ExtentX*2,nixiesTileSet.ExtentY*2);
int digit;
using (D3D.Sprite d3dSprite = new D3D.Sprite(device)) {
d3dSprite.Begin(D3D.SpriteFlags.AlphaBlend);
for (int digitCount = 1; digitCount <= maxScoreDigits; digitCount++) {
digit = score % 10;
score /= 10;
nixiePosition.X = nixiesTileSet.XOrigin + ( digit % nixiesTileSet.NumberFrameColumns ) * nixiesTileSet.ExtentX*2;
nixiePosition.Y = nixiesTileSet.YOrigin; //we know it's only one row
Vector3 position = new Vector3((float)this.Width/2 - digitCount*nixiesTileSet.ExtentX*2,
(float)this.Height-60f, 0f);
d3dSprite.Draw(nixiesTileSet.Texture, nixiePosition,
new Vector3(), position, Color.FromArgb(255,255,255,255));
}
d3dSprite.End();
}
}
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?