-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGame1.cs
1982 lines (1636 loc) · 77.6 KB
/
Game1.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//------------------------------------------------------------
// Microsoft® XNA Game Studio Creator's Guide, Second Edition
// by Stephen Cawood and Pat McGee
// Copyright (c) McGraw-Hill/Osborne. All rights reserved.
// https://www.mhprofessional.com/product.php?isbn=0071614060
//------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using CameraViewer;
using Projectiles;
using KillAllHumansHeightMapImporterTerrainRuntime;
namespace MGHGame
{
/// <summary>
/// This is the driving class for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
//------------------------------------------------------------
// C L A S S L E V E L D E C L A R A T I O N S
//------------------------------------------------------------
// constant definitions
public const float BOUNDARY = 16f;
// accesses drawing methods and properties
GraphicsDeviceManager graphics;
// handle mouse on the PC
#if !XBOX
MouseState mouse;
#endif
// for loading and drawing 2D images on the game window
SpriteBatch spriteBatch;
// load and access PositionColor.fx shader
public Effect positionColorEffect; // shader object
public EffectParameter positionColorEffectWVP; // to set display matrix for window
// load and access Texture.fx shader
private Effect textureEffect; // shader object
private EffectParameter textureEffectWVP; // cumulative matrix w*v*p
private EffectParameter textureEffectImage; // texture parameter
// camera
public Camera cam = new Camera();
// vertex types and buffers
private VertexDeclaration positionColor;
private VertexDeclaration positionColorTexture;
// ground vertices and texture
VertexPositionColorTexture[]
groundVertices = new VertexPositionColorTexture[4];
private Texture2D grassTexture;
private Texture2D blood;
private SoundEffect headshot;
private SoundEffect[] revenge;
SoundEffectInstance sfi;
String highscore_text;
/// <summary>
/// Initializes: -GraphicsDeviceManager object for drawing
/// -ContentManager object for loading media
/// </summary>
Texture2D frontTexture, backTexture, groundTexture,
leftTexture, rightTexture, skyTexture;
private const float EDGE = BOUNDARY * 2f;
private VertexPositionColorTexture[] skyVertices = new VertexPositionColorTexture[4];
private Texture2D spriteTexture;
int frameNum = 1;
private double intervalTime = 0; // time in current interval
private double previousIntervalTime = 0; // interval time at last frame
Model baseModel; Model fanModel;
Matrix[] baseMatrix; Matrix[] fanMatrix;
const int WINDMILL_BASE = 0; const int WINDMILL_FAN = 1;
private float fanRotation = 0.0f; // stores rotation of windmill fan
private Vector3[] bezierA = new Vector3[4]; // route 1
private Vector3[] lineA = new Vector3[2]; // route 2
private Vector3[] bezierB = new Vector3[4]; // route 3
private Vector3[] lineB = new Vector3[2]; // route 4
private Vector3[] bezierC = new Vector3[4]; // route 5
private Vector3[] lineC = new Vector3[2]; // route 6
private float[] keyFrameTime = new float[6];
private float tripTime = 0.0f;
private const float TOTAL_TRIP_TIME = 17.8f;
private const int NUM_KEYFRAMES = 6;
Vector3 currentPosition, previousPosition;
float Yrotation;
Model jetModel;
Matrix[] jetMatrix;
public float BASE_HEIGHT = 0.6f; // start height for models
public Gun gun;
public Model humanModel; public Model humanSpheres;
public Model crateModel; public Model crateSpheres;
const int NUM_BALLOONS = 20;
int NUM_ENEMIES = 100;
bool deadman_walking = false;
#if !XBOX
MouseState mouseCurrent, mousePrevious;
#endif
GamePadState gamepad, gamepadPrevious;
public enum Group
{
plane, bigPlane,
// wall spheres divided into 4 groups
wallBackRight, wallBackLeft, wallFrontLeft, wallFrontRight,
// wall spheres grouped according to location within 4 world quadrants
worldBackRight, worldBackLeft, worldFrontLeft, worldFrontRight
// rocket sphere
,rocketSpheres
}
const int TOTAL_SPHERE_GROUPS = 11;
public Sphere[] sphereGroup = new Sphere[TOTAL_SPHERE_GROUPS];
bool reverse = false;
private SpriteFont spriteFont;
VertexPositionNormalTexture[] terrainVertices;
SoundEffect footstep1;
SoundEffectInstance footstepInstance1;
// You can change this part
public bool SHOW = false; // show bounding spheres
int HP = 100;
const int HIT_SCORE = 1;
const int GETTING_HIT_SCORE = 15;
int enemies_in_a_wave = 1;
float enemy_radius = 10f;
///////////
int score = 0, total_score = 0;
List<int> high_scores = new List<int>();
Enemy enemies;
/// <summary>
/// //////////////////////
/// </summary>
SpriteFont font;
public String GameOver = "NO";
/// <summary>
/// ///////////////
///
/// </summary>
bool positiveDirection = true;
public Random rnd;
public powerup powerups;
// The number of power ups the player has received
int bullet_powerup = 0; int gun_powerup = 0;
public bool game_has_started = false;
SoundEffect ambience, scary_horror, menu_music, haha;
SoundEffectInstance scary_horror_instance, ambienceInstance;
Song game_music, win, lose, menu;
System.Diagnostics.Stopwatch time;
public Vector3 ProjectedXZ(Vector3 position, Vector3 speed,
float directionScalar)
{
// only consider change in X and Z when projecting position
// in neighboring cell.
Vector3 velocity = new Vector3(speed.X, 0.0f, speed.Z);
velocity = Vector3.Normalize(velocity);
float changeX = directionScalar * terrain.cellWidth * velocity.X;
float changeZ = directionScalar * terrain.cellHeight * velocity.Z;
return new Vector3(position.X + changeX, 0.0f, position.Z + changeZ);
}
float CellWeight(Vector3 currentPosition, Vector3 nextPosition)
{
Vector3 currRowColumn = RowColumn(currentPosition);
int currRow = (int)currRowColumn.Z;
int currCol = (int)currRowColumn.X;
Vector3 nextRowColumn = RowColumn(nextPosition);
int nextRow = (int)nextRowColumn.Z;
int nextCol = (int)nextRowColumn.X;
// find row and column between current cell and neighbor cell
int rowBorder, colBorder;
if (currRow < nextRow)
rowBorder = currRow + 1;
else
rowBorder = currRow;
if (currCol < nextCol) // next cell at right of current cell
colBorder = currCol + 1;
else
colBorder = currCol; // next cell at left of current cell
Vector3 intersect = Vector3.Zero; // margins between current
// and next cell
intersect.X = -BOUNDARY + colBorder * terrain.cellWidth;
intersect.Z = -BOUNDARY + rowBorder * terrain.cellHeight;
currentPosition.Y
= 0.0f; // not concerned about height
// find distance between current position and cell border
Vector3 difference = intersect - currentPosition;
float lengthToBorder = difference.Length();
// find distance to projected location in neighboring cell
difference = nextPosition - currentPosition;
float lengthToNewCell = difference.Length();
if (lengthToNewCell == 0) // prevent divide by zero
return 0.0f;
// weighted distance in current cell relative to the entire
// distance to projected position
return lengthToBorder / lengthToNewCell;
}
Vector3 CellNormal(int row, int col)
{
HandleOffHeightMap(ref row, ref col);
return new Vector3(terrain.NormalX(col + row * terrain.NUM_COLS),
terrain.NormalY(col + row * terrain.NUM_COLS),
terrain.NormalZ(col + row * terrain.NUM_COLS));
}
Vector3 Normal(Vector3 position)
{
// coordinates for top left of cell
Vector3 cellPosition = RowColumn(position);
int row = (int)cellPosition.Z;
int col = (int)cellPosition.X;
// distance from top left of cell
float distanceFromLeft = position.X % terrain.cellWidth;
float distanceFromTop = position.Z % terrain.cellHeight;
// use lerp to interpolate normal at point within cell
Vector3 topNormal = Vector3.Lerp(CellNormal(row, col),
CellNormal(row, col + 1),
distanceFromLeft);
Vector3 bottomNormal = Vector3.Lerp(CellNormal(row + 1, col),
CellNormal(row + 1, col + 1),
distanceFromLeft);
Vector3 normal = Vector3.Lerp(topNormal,
bottomNormal,
distanceFromTop);
normal.Normalize(); // convert to unit vector for consistency
if (normal == null || normal == Vector3.Zero)
return Vector3.Up;
return normal;
}
Vector3 NormalWeight(Vector3 position, Vector3 speed,
float numCells, float directionScalar)
{
float weight = 0.0f;
float startWeight = 0.0f;
float totalSteps = (float)numCells;
Vector3 nextPosition;
Vector3 cumulativeNormal = Vector3.Zero;
for (int i = 0; i <= numCells; i++)
{
// get position in next cell
if (speed == Vector3.Zero)
{
speed = cam.view - cam.position;
}
nextPosition = ProjectedXZ(position, speed, directionScalar);
if (i == 0)
{
// current cell
startWeight = CellWeight(position, nextPosition);
weight = startWeight / totalSteps;
}
else if (i == numCells) // end cell
weight = (1.0f - startWeight) / totalSteps;
else // all cells in between
weight = 1.0f / totalSteps;
cumulativeNormal += weight * Normal(position);
position = nextPosition;
}
cumulativeNormal.Normalize();
return cumulativeNormal;
}
Vector3 ProjectedUp(Vector3 position, Vector3 speed, int numCells)
{
Vector3 frontAverage, backAverage, projectedUp;
// total steps must be 0 or more. 0 steps means no shock absorption.
if (numCells <= 0)
return Normal(position);
// weighted average of normals ahead and behind enable smoother ride.
else
{
frontAverage = NormalWeight(position, speed, numCells, 1.0f);
backAverage = NormalWeight(position, speed, numCells, -1.0f);
}
projectedUp = (frontAverage + backAverage) / 2.0f;
projectedUp.Normalize();
return projectedUp;
}
//////////////////
// The core of the game starts here.
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
string[] lines = System.IO.File.ReadAllLines(@"highscores.txt");
foreach (string s in lines)
{
high_scores.Add(Int32.Parse(s));
}
}
// checks to see if the position is in the boundary of the terrain. Then returns a corrected value.
public Vector3 inbound(Vector3 v)
{
if (v.X > BOUNDARY-1) v.X = BOUNDARY-1;
if (v.X < -BOUNDARY+1) v.X = -BOUNDARY+1;
if (v.Z > BOUNDARY-1) v.Z = BOUNDARY-1;
if (v.Z < -BOUNDARY+1) v.Z = -BOUNDARY+1;
return v;
}
// returns true if more than a specific amount of time is passed
float last_time = 0f;
public bool more_than_one_second_has_passed(float sec)
{
if (last_time > time.Elapsed.Seconds) last_time = 0;
if (time.Elapsed.Seconds -last_time > sec)
{
last_time = time.Elapsed.Seconds;
return true;
}
return false;
}
public Vector3 wander(Vector3 direction, Vector3 up)
{
int a = rnd.Next(0, 2) == 1? 1 : -1;
return a * Vector3.Cross(direction, up);
}
public void power_up(String name)
{
if (name == "gun")
gun_powerup++;
if (name == "bullet")
bullet_powerup++;
gun.gun_clip_limit = 13 + gun_powerup * 7;
gun.bullets += bullet_powerup * 20;
bullet_powerup = 0;
}
int enemy_numbers = 0;
public void spawn_enemy_wave(){
if (enemy_numbers >= NUM_ENEMIES && !deadman_walking) return;
if (enemies.active_enemies() < enemies_in_a_wave)
{
for (int i = enemies.active_enemies(); i < enemies_in_a_wave; i++)
{
enemies.spawn_new_enemy(enemy_radius);
enemy_numbers++;
}
}
}
public void stop_all_sounds()
{
MediaPlayer.Stop();
scary_horror_instance.Stop();
ambienceInstance.Stop();
footstepInstance1.Stop();
powerups.stop_sound();
powerups.bullet_box.stop_sounds();
powerups.gun_box.stop_sounds();
}
float got_hit = 0f;
public int adjust_score_and_hp(bool hit_by_enemy)
{
if (hit_by_enemy)
{
HP -= GETTING_HIT_SCORE;
got_hit = 1f;
headshot.Play();
if (sfi.State != SoundState.Playing)
{
int i = rnd.Next(0, revenge.Length);
sfi = revenge[i].CreateInstance();
sfi.Play();
}
}
else
{
score += HIT_SCORE; total_score += HIT_SCORE;
gun.bullets += 20 * (int)(score/20f);
score = score % 20;
}
if (HP <= 0)
{
stop_all_sounds();
GameOver = "LOSE";
game_has_started = false;
if (deadman_walking)
high_scores.Add(total_score);
haha.Play();
MediaPlayer.IsRepeating = false;
MediaPlayer.Play(lose);
return -1; // LOSE
}
else if (enemy_numbers >= NUM_ENEMIES && !deadman_walking)
{
GameOver = "WIN";
game_has_started = false;
if (deadman_walking)
high_scores.Add(total_score);
stop_all_sounds();
MediaPlayer.IsRepeating = false;
MediaPlayer.Play(win);
return 1; // WIN
}
else if (HP <= 30)
{
MediaPlayer.Stop();
scary_horror_instance.Play();
}
return 0;
}
// and it ends here
////////////////////////////////
// implementing heightmap and terrain
Vector3 cam_velocity;
Vector3 lastNormal;
float last_yaw = 0;
void UpdateCameraHeight()
{
const float HOVER_AMOUNT = 0.75f;
float height = CellHeight(cam.position);
cam.view.Y += height - cam.position.Y +HOVER_AMOUNT;
cam.position.Y += height - cam.position.Y + HOVER_AMOUNT;
}
// Importing the heightmap.raw
//
//
Terrain terrain;
private void HandleOffHeightMap(ref int row, ref int col)
{
if (row >= terrain.NUM_ROWS)
row = terrain.NUM_ROWS - 2;
else if (row < 0)
row = 0;
if (col >= terrain.NUM_COLS)
col = terrain.NUM_COLS - 2;
else if (col < 0)
col = 0;
}
Vector3 RowColumn(Vector3 position)
{
// calculate X and Z
int col = (int)((position.X + terrain.worldWidth) / terrain.cellWidth);
int row = (int)((position.Z + terrain.worldHeight) / terrain.cellHeight);
HandleOffHeightMap(ref row, ref col);
return new Vector3(col, 0.0f, row);
}
float Height(int row, int col)
{
HandleOffHeightMap(ref row, ref col);
//return terrainVertices[col + row * terrain.NUM_COLS].Position.Y;
return terrain.position[col + row * terrain.NUM_COLS].Y;
}
float Height2(int row, int col)
{
HandleOffHeightMap(ref row, ref col);
return terrainVertices[col + row * terrain.NUM_COLS].Position.Y;
}
public float CellHeight(Vector3 position)
{
// get top left row and column indices
Vector3 cellPosition = RowColumn(position);
int row = (int)cellPosition.Z;
int col = (int)cellPosition.X;
// distance from top left of cell
float distanceFromLeft, distanceFromTop;
distanceFromLeft = position.X % terrain.cellWidth;
distanceFromTop = position.Z % terrain.cellHeight;
// Lerp projects height relative to known dimensions
float topHeight = MathHelper.Lerp(Height(row, col), Height(row, col + 1), distanceFromLeft);
float bottomHeight = MathHelper.Lerp(Height(row + 1, col), Height(row + 1, col + 1), distanceFromLeft);
return MathHelper.Lerp(topHeight, bottomHeight, distanceFromTop);
}
///////////////////////////////////
// for directional light
//
//
private IndexBuffer indexBuffer; // reference vertices
private VertexBuffer vertexBuffer; // vertex storage
private VertexDeclaration positionNormalTexture;
const int NUM_COLS = 257;
const int NUM_ROWS = 257;
BasicEffect basicEffect;
private void InitializeBasicEffect()
{
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.TextureEnabled = true; // needed if objects are textured
basicEffect.LightingEnabled = true; // must be on for lighting effect
basicEffect.SpecularPower = 5.0f; // highlights
basicEffect.AmbientLightColor = new Vector3(1f, 1f, 1f) * 0.7f; // background light
basicEffect.DirectionalLight0.Enabled = true; // turn on light
basicEffect.DirectionalLight0.DiffuseColor = new Vector3(0.2f, 0.2f, 0.2f); // rgb range 0 to 1
basicEffect.DirectionalLight0.SpecularColor // highlight color
= new Vector3(0.5f, 0.5f, 0.37f); // rgb range 0 to 1
basicEffect.DirectionalLight0.Direction // set normalized direction
= Vector3.Normalize(new Vector3(0.0f, -1.0f, -1.0f));
}
private void InitializeIndices()
{
short[] indices;
indices = new short[2 * NUM_COLS]; // indices for 1 subset
indexBuffer = new IndexBuffer(
graphics.GraphicsDevice, // graphics device
typeof(short), // data type is short
indices.Length, // array size in bytes
BufferUsage.WriteOnly); // memory allocation
// store indices for one subset of vertices
// see Figure 11-2 for the first subset of indices
int counter = 0;
for (int col = 0; col < NUM_COLS; col++)
{
indices[counter++] = (short)col;
indices[counter++] = (short)(col + NUM_COLS);
}
indexBuffer.SetData(indices);
}
private void InitializeVertexBuffer()
{
vertexBuffer = new VertexBuffer(
graphics.GraphicsDevice, // graphics device
typeof(VertexPositionNormalTexture), // vertex type
NUM_COLS * NUM_ROWS, // element count
BufferUsage.WriteOnly); // memory use
// store vertices temporarily while initializing them
VertexPositionNormalTexture[] vertex = new VertexPositionNormalTexture[NUM_ROWS * NUM_COLS];
// set grid width and height
float colWidth = (float)2 * BOUNDARY / (NUM_COLS - 1);
float rowHeight = (float)2 * BOUNDARY / (NUM_ROWS - 1);
// set position, color, and texture coordinates
for (int row = 0; row < NUM_ROWS; row++){
for (int col = 0; col < NUM_COLS; col++){
vertex[col + row * NUM_COLS].Position // position
= terrain.position[col + row * NUM_COLS] ;
float U = (float)col / (float)(NUM_COLS - 1); // UV
float V = (float)row / (float)(NUM_ROWS - 1);
vertex[col + row * NUM_COLS].TextureCoordinate = new Vector2(U, V);
vertex[col + row * NUM_COLS].Normal // normal
= terrain.normal[col + row * NUM_COLS];
}
}
terrainVertices = vertex;
// commit data to vertex buffer
vertexBuffer.SetData(vertex);
}
private void DrawIndexedGrid(Texture2D image, Matrix myWorld)
{
// 1: declare matrices
Matrix world, translate, rotationX, scale, rotationY;
// 2: initialize matrices
// world = myWorld;
world = Matrix.Identity;// camWorldMatrix(cam_velocity, cam.position);
scale = Matrix.CreateScale(2.01f, 2.01f, 2.01f);
// Create two walls with normals that face the user
basicEffect.Texture = image;
// 4: set shader parameters
basicEffect.World = world;
basicEffect.Projection = cam.projectionMatrix;
basicEffect.View = cam.viewMatrix;
// avoid drawing back face for large amounts of vertices
graphics.GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
basicEffect.Techniques[0].Passes[0].Apply();
// 5: draw objet - select vertex type, primitive type, index, & draw
graphics.GraphicsDevice.SetVertexBuffer(vertexBuffer);
graphics.GraphicsDevice.Indices = indexBuffer;
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
graphics.GraphicsDevice.SetVertexBuffer(vertexBuffer);
for (int Z = 0; Z < NUM_ROWS - 1; Z++)
{
graphics.GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleStrip, // primitive
Z * NUM_COLS, // start point in buffer for drawing
0, // minimum vertices in vertex buffer
NUM_COLS * NUM_ROWS, // total vertices in buffer
0, // start point in index buffer
2 * (NUM_COLS - 1)); // primitive count
}
// end shader
// disable culling
graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
}
}
private void LightingShader(PrimitiveType primitiveType)
{
// draw grid one row at a time
for (int Z = 0; Z < NUM_ROWS - 1; Z++)
{
graphics.GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleStrip, // primitive
Z * NUM_COLS, // start point in buffer for drawing
0, // minimum vertices in vertex buffer
NUM_COLS * NUM_ROWS, // total vertices in buffer
0, // start point in index buffer
2 * (NUM_COLS - 1)); // primitive count
}
// end shader
// disable culling
graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
}
//////////////////////////////////////////////////
// collision detection
public bool Collision(BoundingSphere A, BoundingSphere B)
{
if (A.Intersects(B))
{
return true;
}
else
{
return false;
}
}
public bool planeCollision(Camera camera, int rocketGroup, int planeGroup, int rocketNumber)
{
//check selected car and selected wall spheres for collisions
for (int i = 0; i < sphereGroup[planeGroup].sphere.Count; i++)
{
for (int j = 0; j < sphereGroup[rocketGroup].sphere.Count; j++)
{
Matrix transfrom = ScaleSpheres() * TransformPlane(camera);
//generate temp bounding sphere with transformed sphere
BoundingSphere tempPlaneSphere =
sphereGroup[planeGroup].sphere[i].boundingSphere.Transform(transfrom);
tempPlaneSphere.Radius
= SphereScalar() * sphereGroup[planeGroup].sphere[i].boundingSphere.Radius;
transfrom = ScaleSpheres() * gun.transformRocket(rocketNumber);
BoundingSphere tempRocketSphere = sphereGroup[rocketGroup].sphere[i].boundingSphere.Transform(transfrom);
tempRocketSphere.Radius = SphereScalar() * sphereGroup[rocketGroup].sphere[i].boundingSphere.Radius;
if (Collision(tempRocketSphere, tempPlaneSphere))
{
return true;
}
}
}
return false;
}
// Chapter 19 methods:
void InitializeModels()
{
// load models
humanModel = Content.Load<Model>("Models\\fem1");
humanSpheres = Content.Load<Model>("Models\\fem1_sphere");
crateModel = Content.Load<Model>("Models\\crate");
crateSpheres= Content.Load<Model>("Models\\crate_spheres");
}
void WallSphere(Group group, float X, float Z, float radius)
{
Color color = Color.Yellow;
Vector3 position = new Vector3(X, 0.0f, Z);
position.Y = CellHeight(position);
sphereGroup[(int)group].AddSphere(radius, position, color);
}
void InitializeWallSpheres()
{
// initialize vertices around border of world
sphereGroup[(int)Group.wallBackRight] = new Sphere(SHOW);
sphereGroup[(int)Group.wallBackLeft] = new Sphere(SHOW);
sphereGroup[(int)Group.wallFrontRight] = new Sphere(SHOW);
sphereGroup[(int)Group.wallFrontLeft] = new Sphere(SHOW);
Vector3 wall;
const float RADIUS = 0.3f;
const float SPHERE_INCREMENT = 2.0f;
// 1st qtr
wall = new Vector3(BOUNDARY, 0.0f, -BOUNDARY);
// small spheres right and left - back half of world
while (wall.Z < 0.0f)
{
WallSphere(Group.wallBackRight, wall.X, wall.Z, RADIUS);
WallSphere(Group.wallBackLeft, -wall.X, wall.Z, RADIUS);
wall.Z += SPHERE_INCREMENT * RADIUS;
}
// small spheres right and left - front half of world
while (wall.Z < BOUNDARY)
{
WallSphere(Group.wallFrontRight, wall.X, wall.Z, RADIUS);
WallSphere(Group.wallFrontLeft, -wall.X, wall.Z, RADIUS);
wall.Z += SPHERE_INCREMENT * RADIUS;
}
// small spheres right side of world - back and front
wall = new Vector3(BOUNDARY, 0.0f, -BOUNDARY);
while (wall.X > 0.0f)
{
WallSphere(Group.wallBackRight, wall.X, wall.Z, RADIUS);
WallSphere(Group.wallFrontRight, wall.X, -wall.Z, RADIUS);
wall.X -= SPHERE_INCREMENT * RADIUS;
}
// small spheres left side of world - top and bottom
while (wall.X > -BOUNDARY)
{
WallSphere(Group.wallBackLeft, wall.X, wall.Z, RADIUS);
WallSphere(Group.wallBackLeft, wall.X, -wall.Z, RADIUS);
wall.X -= SPHERE_INCREMENT * RADIUS;
}
// separate world into 4 quadrants
sphereGroup[(int)Group.worldBackRight] = new Sphere(SHOW);
sphereGroup[(int)Group.worldBackLeft] = new Sphere(SHOW);
sphereGroup[(int)Group.worldFrontLeft] = new Sphere(SHOW);
sphereGroup[(int)Group.worldFrontRight] = new Sphere(SHOW);
// large spheres - each one surrounds 1 quarter of world
float radius = BOUNDARY;
WallSphere(Group.worldBackRight, BOUNDARY, -BOUNDARY, radius);
WallSphere(Group.worldBackLeft, -BOUNDARY, -BOUNDARY, radius);
WallSphere(Group.worldFrontLeft, -BOUNDARY, BOUNDARY, radius);
WallSphere(Group.worldFrontRight, BOUNDARY, BOUNDARY, radius);
}
void ExtractBoundingSphere(Model tempModel, Color color, int groupNum)
{
// set up model temporarily
Matrix[] tempMatrix = new Matrix[tempModel.Bones.Count];
tempModel.CopyAbsoluteBoneTransformsTo(tempMatrix);
// generate new sphere group
BoundingSphere sphere = new BoundingSphere();
sphereGroup[groupNum] = new Sphere(SHOW);
// store radius, position, and color information for each sphere
foreach (ModelMesh mesh in tempModel.Meshes)
{
sphere = mesh.BoundingSphere;
Vector3 newCenter = sphere.Center;
Matrix transformationMatrix = ScaleModel();
sphereGroup[groupNum].AddSphere(sphere.Radius, sphere.Center, color);
}
}
Matrix ScaleModel()
{
const float SCALAR = 0.002f;
return Matrix.CreateScale(SCALAR, SCALAR, SCALAR);
}
public float SphereScalar()
{
return 0.1f;
}
Matrix ScaleSpheres()
{
// spheres created with different modeling tool so scaled differently
return Matrix.CreateScale(SphereScalar(), SphereScalar(), SphereScalar());
}
void InitializeModelSpheres()
{
// load big plane sphere
Model sphereModel = Content.Load<Model>("Models\\bigPlaneSphere");
ExtractBoundingSphere(sphereModel, Color.Blue, (int)Group.bigPlane);
sphereModel = Content.Load<Model>("Models\\planeSpheres");
ExtractBoundingSphere(sphereModel, Color.White, (int)Group.plane);
sphereModel = Content.Load<Model>("Models\\bulletSphere");
ExtractBoundingSphere(sphereModel, Color.Red, (int)Group.rocketSpheres);
}
private void UpdateKeyframeAnimation(GameTime gameTime)
{
// update total trip time, use modulus to prevent variable overflow
tripTime += (gameTime.ElapsedGameTime.Milliseconds / 1000.0f);
tripTime = tripTime % TOTAL_TRIP_TIME;
// get the current route number from a total of four routes
int routeNum = KeyFrameNumber(reverse);
// sum times for preceding keyframes
float keyFrameStartTime = 0.0f;
for (int i = 0; i < routeNum; i++)
keyFrameStartTime += keyFrameTime[i];
// calculate time spent during current route
float timeBetweenKeys = tripTime - keyFrameStartTime;
// calculate percentage of current route completed
float fraction = timeBetweenKeys / keyFrameTime[routeNum];
// get current X, Y, Z of object being animated
// find point on line or curve by passing in % completed
switch (routeNum)
{
case 0: // first curve
currentPosition = GetPositionOnCurve(bezierA, fraction); break;
case 1: // first line
currentPosition = GetPositionOnLine(lineA, fraction); break;
case 2: // 2nd curve
currentPosition = GetPositionOnCurve(bezierB, fraction); break;
case 3:
currentPosition = GetPositionOnLine(lineB, fraction); break;
case 4:
currentPosition = GetPositionOnCurve(bezierC, fraction); break;
case 5:
currentPosition = GetPositionOnLine(lineC, fraction); break;
}
// get rotation angle about Y based on change in X and Z speed
Vector3 speed = currentPosition - previousPosition;
previousPosition = currentPosition;
Yrotation = (float)Math.Atan2((float)speed.X, (float)speed.Z);
}
private Vector3 GetPositionOnCurve(Vector3[] bezier, float fraction)
{
// returns absolute position on curve based on relative
return // position on curve (relative position ranges from 0% to 100%)
bezier[0] * (1.0f - fraction) * (1.0f - fraction) * (1.0f - fraction) +
bezier[1] * 3.0f * fraction * (1.0f - fraction) * (1.0f - fraction) +
bezier[2] * 3.0f * fraction * fraction * (1.0f - fraction) +
bezier[3] * fraction * fraction * fraction;
}
private Vector3 GetPositionOnLine(Vector3[] line, float fraction)
{
// returns absolute position on line based on relative position
// on curve (relative position ranges from 0% to 100%)
Vector3 lineAtOrigin = line[1] - line[0];
return line[0] + fraction * lineAtOrigin;
}
private int KeyFrameNumber(bool reverse = false)
{
//if (reverse)
//{
// reverse = false;
// return 5 - KeyFrameNumber();
//}
float timeLapsed = 0.0f;
// retrieve current leg of trip
for (int i = 0; i < NUM_KEYFRAMES; i++)
{
if (timeLapsed > tripTime)
return i - 1;
else
timeLapsed += keyFrameTime[i];
}
return 5; // special case for last route
}
Rectangle TitleSafeRegion(Texture2D texture, int numFrames)
{
int windowWidth = Window.ClientBounds.Width;
int windowHeight = Window.ClientBounds.Height;
// some televisions only show 80% of the window
const float UNSAFEAREA = .2f;
const float MARGIN = UNSAFEAREA / 2.0f;
// return bounding margins
int top, left, height, width;
left = (int)(windowWidth * MARGIN);
top = (int)(windowHeight * MARGIN);
width = (int)((1.0f - UNSAFEAREA) * windowWidth - texture.Width);
height = (int)((1.0f - UNSAFEAREA) * windowHeight - texture.Height / numFrames);
return new Rectangle(left, top, width, height);
}
Rectangle TitleSafeRegion(string outputString, SpriteFont font)
{
Vector2 stringDimensions = font.MeasureString(outputString);
float width = stringDimensions.X; // string pixel width
float height = stringDimensions.Y; // font pixel height
// some televisions only show 80% of the window
const float UNSAFEAREA = 0.2f;
Vector2 topLeft = new Vector2();
topLeft.X = graphics.GraphicsDevice.Viewport.Width * UNSAFEAREA / 2.0f;