Jypeli  5
The simple game programming library
Layer.cs
Siirry tämän tiedoston dokumentaatioon.
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
5 using System.Runtime.CompilerServices;
6 using Microsoft.Xna.Framework;
7 using Microsoft.Xna.Framework.Graphics;
8 using Jypeli.Effects;
9 
10 using XnaRectangle = Microsoft.Xna.Framework.Rectangle;
11 using XnaColor = Microsoft.Xna.Framework.Color;
12 
13 namespace Jypeli
14 {
18  public enum DrawOrder
19  {
24  Irrelevant,
25 
30  }
31 
32 
36  public class Layer : Updatable
37  {
41  static readonly Vector[] squareVertices =
42  {
43  new Vector( -0.5, -0.5 ),
44  new Vector( -0.5, 0.5 ),
45  new Vector( 0.5, -0.5 ),
46  new Vector( 0.5, 0.5 )
47  };
48 
52  static readonly Int16[] squareIndices =
53  {
54  0, 1, 2,
55  2, 1, 3
56  };
57 
61  static readonly Vector[] triangleVertices =
62  {
63  new Vector( 0f, 0.5f ),
64  new Vector( 0.5f, -0.5f ),
65  new Vector( -0.5f, -0.5f ),
66  };
67 
71  static readonly Int16[] triangleIndices = { 0, 1, 2 };
72 
73  static readonly TextureCoordinates defaultCoords = new TextureCoordinates()
74  {
75  TopLeft = new Vector2(0.0f, 0.0f),
76  TopRight = new Vector2(1.0f, 0.0f),
77  BottomLeft = new Vector2(0.0f, 1.0f),
78  BottomRight = new Vector2(1.0f, 1.0f),
79  };
80 
82  private List<ParticleSystem> effects = new List<ParticleSystem>();
83 
84  private List<IGameObject> objectsWithImage = new List<IGameObject>();
85  private List<IGameObject> objectsWithoutImage = new List<IGameObject>();
86  private List<IGameObject> objectsWithDrawMethod = new List<IGameObject>();
87 
88  private Vector _relativeTransition = new Vector(1, 1);
89 
90  public bool IsUpdated
91  {
92  get { return true; }
93  }
94 
98  public DrawOrder DrawOrder { get; set; }
99 
100 #if DEBUG
101  [EditorBrowsable(EditorBrowsableState.Never)]
102  internal DoubleMeter ObjectCount = new DoubleMeter(0.0) { MaxValue = 10000 };
103 #endif
104 
110  {
111  get { return _relativeTransition; }
112  set { _relativeTransition = value; }
113  }
114 
118  public bool IgnoresZoom { get; set; }
119 
124  public Grid Grid { get; set; }
125 
126 
127  public Layer()
128  {
129  DrawOrder = DrawOrder.Irrelevant;
130  Objects.ItemAdded += ObjectAdded;
131  Objects.ItemRemoved += ObjectRemoved;
132  }
133 
138  public static Layer CreateStaticLayer()
139  {
140  return new Layer() { RelativeTransition = Vector.Zero, IgnoresZoom = true };
141  }
142 
143  private void ObjectAdded( IGameObject obj )
144  {
145  if ( obj is ParticleSystem )
146  effects.Add( (ParticleSystem)obj );
147 
148  if ( obj is CustomDrawable )
149  {
150  objectsWithDrawMethod.Add( obj );
151  }
152  else if ( obj.Image != null )
153  {
154  var hash = obj.Image.GetHashCode();
155  var index = objectsWithImage.FindLastIndex( o => o.Image.GetHashCode() == hash );
156 
157  if ( index == -1 )
158  {
159  objectsWithImage.Add( obj );
160  }
161  else
162  {
163  // Optimize drawing by inserting after the last same image
164  objectsWithImage.Insert( index + 1, obj );
165  }
166  }
167  else
168  {
169  objectsWithoutImage.Add( obj );
170  }
171 
172  ( (IGameObjectInternal)obj ).Layer = this;
173  }
174 
175  private void ObjectRemoved( IGameObject obj )
176  {
177  if ( obj is ParticleSystem )
178  effects.Remove( (ParticleSystem)obj );
179  objectsWithDrawMethod.Remove( obj );
180  objectsWithImage.Remove( obj );
181  objectsWithoutImage.Remove( obj );
182  ( (IGameObjectInternal)obj ).Layer = null;
183  }
184 
185  internal void Add(IGameObject o)
186  {
187  Objects.Add(o);
188  }
189 
190  internal void Remove( IGameObject o )
191  {
192  Objects.Remove( o );
193  }
194 
195  public void Clear()
196  {
197  Objects.Clear();
198  effects.Clear();
199  objectsWithImage.Clear();
200  objectsWithoutImage.Clear();
201  objectsWithDrawMethod.Clear();
202  }
203 
204  public void Update( Time time )
205  {
206 #if DEBUG
207  ObjectCount.Value = Objects.Count;
208 #endif
209  Objects.Update( time );
210 
211  for (int i = 0; i < effects.Count; i++)
212  {
213  effects[i].Update(time);
214  }
215 
216  }
217 
218  internal void Draw(Camera camera)
219  {
220  var zoomMatrix = IgnoresZoom ? Matrix.Identity : Matrix.CreateScale( (float)( camera.ZoomFactor ), (float)( camera.ZoomFactor ), 1f );
221  var worldMatrix =
222  Matrix.CreateTranslation((float)(-camera.Position.X * RelativeTransition.X), (float)(-camera.Position.Y * RelativeTransition.Y), 0)
223  * zoomMatrix;
224 
225  switch (DrawOrder)
226  {
227  case DrawOrder.Irrelevant:
228  DrawEfficientlyInNoParticularOrder(ref worldMatrix);
229  break;
230  case DrawOrder.FirstToLast:
231  DrawInOrderFromFirstToLast(ref worldMatrix);
232  break;
233  default:
234  break;
235  }
236 
237  for (int i = 0; i < effects.Count; i++)
238  {
239  effects[i].Draw(worldMatrix);
240  }
241 
242  if ( Grid != null )
243  {
244  DrawGrid( ref worldMatrix );
245  }
246  }
247 
248  private void DrawGrid( ref Matrix matrix )
249  {
250  Graphics.LineBatch.Begin( ref matrix );
251 
252  var camera = Game.Instance.Camera;
253  var screen = Game.Screen;
254  Vector topLeft = camera.ScreenToWorld( new Vector( screen.Left, screen.Top ) );
255  Vector topRight = camera.ScreenToWorld( new Vector( screen.Right, screen.Top ) );
256  Vector bottomLeft = camera.ScreenToWorld( new Vector( screen.Left, screen.Bottom ) );
257  Vector bottomRight = camera.ScreenToWorld( new Vector( screen.Right, screen.Bottom ) );
258 
259  int horizontalCount = (int)Math.Ceiling( ( topRight.X - topLeft.X ) / Grid.CellSize.X );
260  int leftmostLine = (int)Math.Ceiling( topLeft.X / Grid.CellSize.X );
261  double leftmostX = leftmostLine * Grid.CellSize.X;
262 
263  for ( int i = 0; i < horizontalCount; i++ )
264  {
265  double x = leftmostX + i * Grid.CellSize.X;
266  Vector startPoint = new Vector( x, topLeft.Y );
267  Vector endPoint = new Vector( x, bottomLeft.Y );
268  Graphics.LineBatch.Draw( startPoint, endPoint, Grid.Color );
269  }
270 
271  int verticalCount = (int)Math.Ceiling( ( topRight.Y - bottomRight.Y ) / Grid.CellSize.Y );
272  int bottommostLine = (int)Math.Ceiling( bottomRight.Y / Grid.CellSize.Y );
273  double bottommostY = bottommostLine * Grid.CellSize.Y;
274 
275  for ( int i = 0; i < verticalCount; i++ )
276  {
277  double y = bottommostY + i * Grid.CellSize.Y;
278 
279  // Doesn't draw the line when y is 0! Wonder why...
280  if ( y == 0.0 ) y += 0.1;
281 
282  Vector startPoint = new Vector( bottomLeft.X, y );
283  Vector endPoint = new Vector( bottomRight.X, y );
284  Graphics.LineBatch.Draw( startPoint, endPoint, Grid.Color );
285  }
286 
287  Graphics.LineBatch.End();
288  }
289 
290  private void DrawInOrderFromFirstToLast(ref Matrix worldMatrix)
291  {
292  Renderer.LightingEnabled = true;
293 
294  for (int i = 0; i < Objects.Count; i++)
295  {
296  var o = Objects[i];
297 
298  if ( o is CustomDrawable )
299  {
300  if ( o.IsVisible ) ( (CustomDrawable)o ).Draw( worldMatrix );
301  }
302  else
303  {
304  Renderer.LightingEnabled = !o.IgnoresLighting;
305  Draw( o, ref worldMatrix );
306  }
307  }
308 
309  DrawChildObjects( worldMatrix );
310 
311  Renderer.LightingEnabled = false;
312  }
313 
314  private void DrawEfficientlyInNoParticularOrder(ref Matrix worldMatrix)
315  {
316  Renderer.LightingEnabled = true;
317  DrawObjectsWithoutImages( worldMatrix );
318  DrawObjectsWithImages( worldMatrix );
319  DrawCustomDrawables( worldMatrix );
320  DrawChildObjects( worldMatrix );
321  Renderer.LightingEnabled = false;
322  }
323 
324  private void DrawObjectsWithoutImages( Matrix worldMatrix )
325  {
326  Graphics.ShapeBatch.Begin( ref worldMatrix );
327  for (int i = 0; i < objectsWithoutImage.Count; i++)
328  {
329  var o = objectsWithoutImage[i];
330 
331  if ( !o.IsVisible )
332  continue;
333 
334  bool hasChildObjects = o is GameObject && ( (GameObject)o )._childObjects != null;
335  bool isSimple = !hasChildObjects && !o.TextureFillsShape;
336 
337  Renderer.LightingEnabled = !o.IgnoresLighting;
338 
339  if ( isSimple && ( o.Image == null ) && ( o.Shape == Shape.Rectangle || o.Shape == Shape.Triangle ) )
340  {
341  DrawShape( o, ref worldMatrix );
342  }
343  else
344  {
345  Draw( o, ref worldMatrix );
346  }
347  }
348  Graphics.ShapeBatch.End();
349  }
350 
351  private void DrawObjectsWithImages( Matrix worldMatrix )
352  {
353  // Passing null for image here does not hurt, because the first
354  // ReferenceEquals-comparison calls End() without drawing a single
355  // image.
356  Graphics.ImageBatch.Begin( ref worldMatrix, null );
357 
358  Image previousImage = null;
359 
360  for (int i = 0; i < objectsWithImage.Count; i++)
361  {
362  var o = objectsWithImage[i];
363 
364  if ( !o.IsVisible )
365  continue;
366 
367  bool hasChildObjects = o is GameObject && ( (GameObject)o )._childObjects != null;
368  bool isSimple = !hasChildObjects && !o.TextureFillsShape;
369 
370  Renderer.LightingEnabled = !o.IgnoresLighting;
371 
372  if ( isSimple && ( o.Image != null ) )
373  {
374  if ( !Object.ReferenceEquals( o.Image, previousImage ) )
375  {
376  // Object o has different image than the previous one,
377  // so let's start a new batch with the new image. The objects
378  // should be sorted at this point.
379  Graphics.ImageBatch.End();
380  Graphics.ImageBatch.Begin( ref worldMatrix, o.Image.XNATexture );
381  previousImage = o.Image;
382  }
383  DrawTexture( o, ref worldMatrix );
384  }
385  else
386  {
387  Draw( o, ref worldMatrix );
388  }
389  }
390 
391  Graphics.ImageBatch.End();
392  }
393 
394  private void DrawCustomDrawables( Matrix worldMatrix )
395  {
396  for (int i = 0; i < objectsWithDrawMethod.Count; i++)
397  {
398  var o = (CustomDrawable)objectsWithDrawMethod[i];
399  if ( o.IsVisible )
400  {
401  o.Draw( worldMatrix );
402  }
403  }
404  }
405 
406  private void DrawChildObjects( Matrix worldMatrix )
407  {
408  Vector drawScale = new Vector( 1, 1 );
409 
410  for ( int i = 0; i < Objects.Count; i++ )
411  {
412  var go = Objects[i] as GameObject;
413  if ( go == null || go._childObjects == null || go is Jypeli.Widgets.Window )
414  continue;
415 
416  if ( go.Shape.IsUnitSize )
417  drawScale = go.Size;
418 
419  Vector2 position = new Vector2( (float)go.Position.X, (float)go.Position.Y );
420  Vector2 scale = new Vector2( (float)drawScale.X, (float)drawScale.Y );
421  float rotation = go.RotateImage ? (float)go.Angle.Radians : 0;
422 
423  Matrix childTransformation =
424  Matrix.CreateRotationZ( rotation )
425  * Matrix.CreateTranslation( position.X, position.Y, 0f )
426  * worldMatrix;
427 
428  // light positioning for child objects has not been implemented, so
429  // let's not use it.
430  Renderer.LightingEnabled = false;
431 
432  for ( int j = 0; j < go._childObjects.Count; j++ )
433  {
434  Draw( go._childObjects[j], ref childTransformation );
435  }
436 
437  Renderer.LightingEnabled = true;
438  }
439  }
440 
441  private void DrawTexture(IGameObject o, ref Matrix parentTransformation)
442  {
443  Vector2 position = new Vector2((float)o.Position.X, (float)o.Position.Y);
444  Vector2 scale = new Vector2((float)o.Size.X, (float)o.Size.Y);
445  float rotation = o.RotateImage ? (float)o.Angle.Radians : 0;
446 
447  if (o.IsVisible)
448  {
449  if ( o.TextureWrapSize == Vector.Diagonal )
450  {
451  Graphics.ImageBatch.Draw( defaultCoords, position, scale, rotation );
452  }
453  else
454  {
455  float wx = (float)( Math.Sign( o.TextureWrapSize.X ) );
456  float wy = (float)( Math.Sign( o.TextureWrapSize.Y ) );
457  float left = (float)( -wx / 2 + 0.5 );
458  float right = (float)( wx / 2 + 0.5 );
459  float top = (float)( -wy / 2 + 0.5 );
460  float bottom = (float)( wy / 2 + 0.5 );
461 
462  TextureCoordinates customCoords = new TextureCoordinates()
463  {
464  TopLeft = new Vector2( left, top ),
465  TopRight = new Vector2( right, top ),
466  BottomLeft = new Vector2( left, bottom ),
467  BottomRight = new Vector2( right, bottom ),
468  };
469 
470  if ( o.TextureWrapSize.X == wx && o.TextureWrapSize.Y == wy )
471  {
472  // Draw only once
473  Graphics.ImageBatch.Draw( customCoords, position, scale, rotation );
474  return;
475  }
476 
477  float topLeftX = -(float)( o.TextureWrapSize.X - 1 ) / 2;
478  float topLeftY = -(float)( o.TextureWrapSize.Y - 1 ) / 2;
479 
480  Vector2 newScale = new Vector2(
481  scale.X / ( wx * (float)o.TextureWrapSize.X ),
482  scale.Y / ( wy * (float)o.TextureWrapSize.Y ) );
483 
484  for ( int y = 0; y < o.TextureWrapSize.Y; y++ )
485  {
486  for ( int x = 0; x < o.TextureWrapSize.X; x++ )
487  {
488  Vector2 newPosition = position + new Vector2( ( topLeftX + x ) * newScale.X, ( topLeftY + y ) * newScale.Y );
489  Graphics.ImageBatch.Draw( customCoords, newPosition, newScale, rotation );
490  }
491  }
492  }
493  }
494  }
495 
496  private void DrawShape(IGameObject o, ref Matrix parentTransformation)
497  {
498  Vector2 position = new Vector2((float)o.Position.X, (float)o.Position.Y);
499  Vector2 scale = new Vector2((float)o.Size.X, (float)o.Size.Y);
500  float rotation = (float)o.Angle.Radians;
501 
502  if (o.IsVisible)
503  {
504  Vector[] vertices;
505  Int16[] indices;
506 
507  if (o.Shape == Shape.Rectangle)
508  {
509  vertices = squareVertices;
510  indices = squareIndices;
511  }
512  else if (o.Shape == Shape.Triangle)
513  {
514  vertices = triangleVertices;
515  indices = triangleIndices;
516  }
517  else
518  {
519  vertices = new Vector[] { };
520  indices = new Int16[] { };
521  }
522 
523  Graphics.ShapeBatch.Draw(vertices, indices, o.Color, position, scale, rotation);
524  }
525  }
526 
527  private void Draw(IGameObject o, ref Matrix parentTransformation)
528  {
529  Vector drawScale = new Vector(1, 1);
530  if (o.Shape.IsUnitSize)
531  drawScale = o.Size;
532 
533  Vector2 position = new Vector2((float)o.Position.X, (float)o.Position.Y);
534  Vector2 scale = new Vector2((float)drawScale.X, (float)drawScale.Y);
535  float rotation = o.RotateImage ? (float)o.Angle.Radians : 0;
536 
537  if (o.IsVisible)
538  {
539  Matrix transformation =
540  Matrix.CreateScale(scale.X, scale.Y, 1f)
541  * Matrix.CreateRotationZ(rotation)
542  * Matrix.CreateTranslation(position.X, position.Y, 0f)
543  * parentTransformation;
544 
545  if (o is CustomDrawable)
546  {
547  ((CustomDrawable)o).Draw(parentTransformation);
548  }
549  else if (o.Image != null && (!o.TextureFillsShape))
550  {
551  Renderer.DrawImage(o.Image, ref transformation, o.TextureWrapSize);
552  }
553  else if (o.Image != null)
554  {
555  Renderer.DrawShape(o.Shape, ref transformation, ref transformation, o.Image, o.TextureWrapSize, o.Color);
556  }
557  else
558  {
559  Renderer.DrawShape(o.Shape, ref transformation, o.Color);
560  }
561  }
562  }
563 
564  internal void GetObjectsAboutToBeAdded( List<IGameObject> result )
565  {
566  result.AddRange( Objects.GetObjectsAboutToBeAdded() );
567  }
568  }
569 }
static void DrawShape(Shape shape, ref Matrix transformation, ref Matrix textureTransformation, Image texture, Vector textureWrapSize, Color color)
Piirtää kuvion niin, että tekstuuri täyttää sen.
Definition: Renderer.cs:285
Jypelin sisäiset metodit ja propertyt joihin käyttäjän ei tarvitse päästä käsiksi kuuluvat tähän luokkaan...
Definition: IGameObject.cs:84
void Clear()
Definition: Layer.cs:195
Järjestelmä partikkelien käsittelyyn
Kuvio.
Definition: Shapes.cs:48
static Layer CreateStaticLayer()
Luo staattisen kerroksen (ei liiku kameran mukana)
Definition: Layer.cs:138
bool IsUpdated
Definition: Layer.cs:91
DrawOrder
Piirtojärjestys.
Definition: Layer.cs:18
Vector RelativeTransition
Kuinka paljon tämän kerroksen olioiden paikka muuttuu kameran siirtyessä suhteessa muihin kerroksiin...
Definition: Layer.cs:110
static void DrawImage(Image texture, ref Matrix matrix, Vector wrapSize)
Definition: Renderer.cs:126
void Update(Time time)
Lisää ja poistaa jonossa olevat elementit sekä kutsuu niiden Update-metodia.
static readonly Rectangle Rectangle
Suorakulmio.
Definition: Shapes.cs:72
double ZoomFactor
Kameran zoomauskerroin. Oletuksena 1.0. Mitä suurempi zoomauskerroin, sitä lähempänä kamera on (esim ...
Definition: Camera.cs:99
Image Image
Olion kuva. Voi olla null, jolloin piirretään vain väri.
Rajapinta päivittyville olioille.
Definition: Updatable.cs:6
Color Color
Definition: Grid.cs:8
static Game Instance
Definition: Game.cs:149
Rajapinta olioille, joilla on oma Draw-metodi.
static readonly Vector Zero
Nollavektori.
Definition: Vector.cs:61
Mittari, joka mittaa double-tyyppisiä arvoja. Sidottavissa näyttöihin, kuten ValueDisplay ja BarGauge...
Definition: Meter.cs:515
Sisältää tiedon ajasta, joka on kulunut pelin alusta ja viime päivityksestä.
Definition: Time.cs:13
Kamera. Määrittää mikä osa pelitasosta on kerralla näkyvissä.
Definition: Camera.cs:42
Kuva.
Definition: Image.cs:24
Vector TextureWrapSize
Definition: IGameObject.cs:54
static readonly Triangle Triangle
Tasasivuinen kolmio.
Definition: Shapes.cs:77
static ScreenView Screen
Näytön dimensiot, eli koko ja reunat.
Definition: Game.cs:194
Vector Position
Kameran sijainti.
Definition: Camera.cs:56
double Y
Definition: Vector.cs:275
bool IgnoresZoom
Jättää kameran zoomin huomiotta jos asetettu.
Definition: Layer.cs:118
Peliluokka reaaliaikaisille peleille.
Definition: DebugScreen.cs:10
void Update(Time time)
Definition: Layer.cs:204
Kerros. Vastaa olioiden piirtämisestä.
Definition: Layer.cs:36
double X
Definition: Vector.cs:274
static readonly Vector Diagonal
Diagonaalivektori (1,1)
Definition: Vector.cs:76
double Radians
Palauttaa tai asettaa kulman radiaaneina.
Definition: Angle.cs:86
static bool LightingEnabled
Definition: Renderer.cs:73
Oliot piirretään siinä järjestyksessä missä ne on lisätty peliin.
Action< T > ItemAdded
Tapahtuu kun uusi elementti on lisätty listaan.
int Count
Kuinka monta elementtiä listassa nyt on. Ei laske mukaan samalla päivityskierroksella tehtyjä muutoks...
Yhteinen rajapinta kaikille peliolioille.
Definition: IGameObject.cs:14
Luokka, joka sisältää metodeita kuvioiden ja tekstuurien piirtämiseen 2D-tasossa. ...
Definition: Renderer.cs:51
Synkroninen lista, eli lista joka päivittyy vasta kun sen Update-metodia kutsutaan. Jos listalle lisätään IUpdatable-rajapinnan toteuttavia olioita, kutsutaan myös niiden Update-metodeja samalla.
Camera Camera
Kamera, joka näyttää ruudulla näkyvän osan kentästä. Kameraa voidaan siirtää, zoomata tai asettaa seu...
Definition: Game.cs:166
Action< T > ItemRemoved
Tapahtuu kun elementti on poistettu listasta.
2D-vektori.
Definition: Vector.cs:56
Vector CellSize
Definition: Grid.cs:9
Pelialueella liikkuva olio. Käytä fysiikkapeleissä PhysicsObject-olioita.
Definition: __GameObject.cs:54
override int GetHashCode()
Definition: Image.cs:274
abstract bool IsUnitSize
If true, the shape must be scaled by the size of the object that has the shape. Typically, an unit-sized object has width and height of 1.0.
Definition: Shapes.cs:55