Jypeli  5
The simple game programming library
PlatformCharacter2.cs
Siirry tämän tiedoston dokumentaatioon.
1 
2 #if DEBUG
3 // #define VISUALIZE
4 #endif
5 
6 using System;
7 using System.ComponentModel;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Jypeli;
11 using Jypeli.Assets;
12 using Physics2DDotNet;
13 
14 
19 {
20  private int _tolerance = 5;
21  private double _accel = 1000;
22  private double _maxVel = 500;
23 
24  private enum PlatformCharacterState { Idle, Falling, Jumping }
25  private PlatformCharacterState state = PlatformCharacterState.Idle;
26 
27  private Weapon weapon = null;
28  private Vector _cachedGravity = Vector.Zero;
29  private double _cachedGravityMagnitude = 0;
30  private Vector _cachedGravityNormal = Vector.Zero;
31 
32  private bool collisionDetected = false;
33  private int noCollisionCount = 0;
34  private int walkSteps = 0;
35  private Direction _curDirection = Direction.Right;
36  private Timer detachTimer = new Timer();
37 
38  private bool customAnimPlaying = false;
39  private Action customAnimAction;
40 
41  private double gravityMagnitude
42  {
43  get
44  {
45  updateGravity();
46  return _cachedGravityMagnitude;
47  }
48  }
49 
50  private Vector gravityNormal
51  {
52  get
53  {
54  updateGravity();
55  return _cachedGravityNormal;
56  }
57  }
58 
63  public int PlatformTolerance
64  {
65  get { return _tolerance; }
66  set { _tolerance = value; }
67  }
68 
73  {
74  get { return _curDirection; }
75  set { Turn( value ); }
76  }
77 
81  public double Acceleration
82  {
83  get { return _accel; }
84  set { _accel = value; }
85  }
86 
90  public double MaxVelocity
91  {
92  get { return _maxVel; }
93  set { _maxVel = value; }
94  }
95 
96 #if VISUALIZE
97  private GameObject ssurface = new GameObject( 1, 2, Shape.Rectangle ) { Color = Color.Blue };
98  private PhysicsObject _platform = null;
99 
103  public PhysicsObject Platform
104  {
105  get { return _platform; }
106  set
107  {
108  if ( value != null )
109  {
110  Vector distNormal = this.Position - value.Position;
111  double sideWidth = Math.Sqrt( Width * Width + Height * Height );
112  ssurface.Position = this.Position - Vector.FromLengthAndAngle( sideWidth, distNormal.Angle );
113  ssurface.Color = Color.Red;
114  }
115  else
116  {
117  ssurface.Color = Color.Blue;
119  }
120 
121  _platform = value;
122  }
123  }
124 #else
125  public PhysicsObject Platform { get; set; }
129 #endif
130 
135  public Vector PlatformNormal { get; private set; }
136 
140  public Weapon Weapon
141  {
142  get { return weapon; }
143  set
144  {
145  // First check: same weapon
146  if ( weapon == value ) return;
147 
148  // Remove the previous weapon if any
149  if ( weapon != null )
150  {
151  // Reset the weapon when removing
152 
153  if ( !IsWeaponFacingRight() )
154  {
155  weapon.X *= -1;
156  weapon.TextureWrapSize = new Vector( 1, 1 );
157  }
158 
159  weapon.Angle = Angle.Zero;
160  this.Remove( weapon );
161  }
162 
163  this.weapon = value;
164  if ( value == null )
165  return;
166 
167  this.Add( value );
168 
169  if ( FacingDirection == Direction.Left )
170  {
171  // If facing left, set the weapon to match the direction
172  weapon.X *= -1;
173  weapon.Angle = Angle.StraightAngle;
174  weapon.TextureWrapSize = new Vector( 1, -1 );
175  }
176  }
177  }
178 
182  public bool CanMoveOnAir { get; set; }
183 
187  public Animation AnimWalk { get; set; }
188 
192  public Animation AnimJump { get; set; }
193 
197  public Animation AnimFall { get; set; }
198 
202  public bool LoopJumpAnim { get; set; }
203 
207  public bool LoopFallAnim { get; set; }
208 
212  public Animation AnimIdle { get; set; }
213 
217  public bool WalkOnAir { get; set; }
218 
222  public event Action<Direction> DirectionChanged;
223 
227  public PlatformCharacter2(double width, double height)
228  : this(width, height, Shape.Circle)
229  {
230  }
231 
235  public PlatformCharacter2( double width, double height, Shape shape )
236  : base( width, height, shape /*, CollisionShapeQuality.FromValue(0.7)*/ )
237  {
238  KineticFriction = 0.0;
239  Restitution = 0.2;
240  CanRotate = false;
241  CanMoveOnAir = true;
242 
243  // This avoids high speeds, particularly when falling. This then avoids
244  // going through objects.
245  LinearDamping = 0.96;
246 
247  AddedToGame += AddCollisionHandler;
248  IsUpdated = true;
249  }
250 
251  private void SetAnimation( Animation anim, bool loop = true )
252  {
253  if ( customAnimPlaying || anim == null || Animation == anim || anim.FrameCount == 0 )
254  return;
255 
256  Animation = anim;
257 
258  if ( loop )
259  Animation.Start();
260  else
261  AnimJump.Start( 1 );
262  }
263 
264  public void PlayAnimation( Animation anim, Action onPlayed = null )
265  {
266  customAnimPlaying = true;
267  customAnimAction = onPlayed;
268  this.Animation = anim;
269  anim.Played += AnimationPlayed;
270  anim.Start( 1 );
271  }
272 
273  private void AnimationPlayed()
274  {
275  Animation.Played -= AnimationPlayed;
276  customAnimPlaying = false;
277 
278  if ( customAnimAction != null )
279  customAnimAction();
280  }
281 
282  private void updateGravity()
283  {
284  PhysicsGame physGame = Game.Instance as PhysicsGame;
285  if ( physGame == null ) return;
286 
287  if ( physGame.Gravity != _cachedGravity )
288  {
289  _cachedGravity = physGame.Gravity;
290  _cachedGravityMagnitude = _cachedGravity.Magnitude;
291  _cachedGravityNormal = _cachedGravity / _cachedGravityMagnitude;
292 
293 #if VISUALIZE
294  ssurface.Angle = gravityNormal.LeftNormal.Angle;
295 #endif
296  }
297  }
298 
299  private void AddCollisionHandler()
300  {
301  PhysicsGameBase physicsGame = Game.Instance as PhysicsGameBase;
302  if ( physicsGame == null ) throw new InvalidOperationException( "Cannot have a platform character in non-physics game" );
303 
304  this.Body.Colliding += delegate( object sender, CollisionEventArgs args )
305  {
306  PhysicsObject other = (PhysicsObject)args.Other.Tag;
307  Vector normal = new Vector( args.Contact.Points[0].Normal.X, args.Contact.Points[0].Normal.Y );
308  OnColliding( this, other, normal );
309  };
310 
311  #if VISUALIZE
312  ssurface.Width = 2 * Math.Sqrt( Game.Level.Width * Game.Level.Width + Game.Level.Height * Game.Level.Height );
313  Game.Instance.Add( ssurface );
314  #endif
315  }
316 
320  public void Walk()
321  {
322  walkSteps++;
323 
324  if ( state == PlatformCharacterState.Idle || WalkOnAir )
325  SetAnimation( AnimWalk );
326  }
327 
332  public void Walk( Direction direction )
333  {
334  Turn( direction );
335  walkSteps++;
336 
337  if ( state == PlatformCharacterState.Idle || WalkOnAir )
338  SetAnimation( AnimWalk );
339  }
340 
345  public void Turn( Direction direction )
346  {
347  if ( direction == FacingDirection || ( direction != Direction.Left && direction != Direction.Right ) )
348  return;
349 
350  walkSteps = 0;
352 
353  if ( Weapon != null )
354  {
355  Weapon.X *= -1;
358  }
359 
360  _curDirection = direction;
361 
362  if ( DirectionChanged != null )
363  {
364  DirectionChanged( direction );
365  }
366  }
367 
371  public void StopWalking()
372  {
373  walkSteps = 0;
374  if ( state == PlatformCharacterState.Idle )
375  SetAnimation( AnimIdle );
376  }
377 
378  private bool IsWeaponFacingRight()
379  {
380  return (-Math.PI / 2) < Weapon.Angle.Radians
381  && Weapon.Angle.Radians < (Math.PI / 2);
382  }
383 
388  public void Jump( double speed )
389  {
390  if ( Platform == null || state == PlatformCharacterState.Jumping ) return;
391  ForceJump( speed );
392  }
393 
398  public void ForceJump( double speed )
399  {
400  IgnoresGravity = false;
401  this.Hit( Mass * speed * -gravityNormal );
402  Platform = null;
403 
404  state = PlatformCharacterState.Jumping;
405  SetAnimation( AnimJump, LoopJumpAnim );
406 
407  Timer t = new Timer();
408  t.Interval = 0.01;
409  t.Timeout += delegate
410  {
411  if ( this.Velocity.Y < 0 )
412  {
413  t.Stop();
414  state = PlatformCharacterState.Falling;
415  SetAnimation( AnimFall, LoopFallAnim );
416  }
417  };
418  t.Start();
419  }
420 
421  protected override void PrepareThrowable( PhysicsObject obj, Angle angle, double force, double distanceDelta, double axialDelta )
422  {
423  double d = ( this.Width + obj.Width ) / 2;
424  Angle throwAngle = FacingDirection == Direction.Left ? Angle.StraightAngle - angle : angle;
425  obj.Position = this.AbsolutePosition + this.FacingDirection.GetVector() * d + gravityNormal * axialDelta;
426  obj.Hit( Vector.FromLengthAndAngle( force, throwAngle ) );
427  }
428 
429  private void OnColliding( PhysicsObject collisionHelperObject, PhysicsObject target, Vector normal )
430  {
431  if ( target.IgnoresCollisionResponse )
432  return;
433 
434  double dot = Vector.DotProduct( normal, gravityNormal );
435  if ( Math.Abs( dot ) < 0.5 )
436  return;
437 
438  if ( Platform != null && Platform != target )
439  return;
440 
441  if ( Platform == null )
442  Platform = target;
443 
444  collisionDetected = true;
445  noCollisionCount = 0;
446  PlatformNormal = normal;
447 
448  if ( state == PlatformCharacterState.Falling )
449  state = PlatformCharacterState.Idle;
450  }
451 
457  [EditorBrowsable(EditorBrowsableState.Never)]
458  public override void Update(Time time)
459  {
460  if ( Platform != null && !collisionDetected )
461  {
462  if ( ++noCollisionCount > PlatformTolerance )
463  {
464  Platform = null;
465 
466  if ( state != PlatformCharacterState.Jumping )
467  {
468  state = PlatformCharacterState.Falling;
469  SetAnimation( AnimFall, LoopFallAnim );
470  }
471  }
472  }
473 
474  collisionDetected = false;
475 
476  if ( walkSteps > 0 )
477  {
478  // Walking
479  double impulse = Mass * Acceleration * time.SinceLastUpdate.TotalSeconds;
480  Vector unitX = gravityMagnitude > 0 ? gravityNormal.LeftNormal : Vector.UnitX;
481 
482  if ( _curDirection == Direction.Left && -Velocity.X < MaxVelocity )
483  this.Push( Mass * Acceleration * -unitX );
484  else if ( _curDirection == Direction.Right && Velocity.X < MaxVelocity )
485  this.Push( Mass * Acceleration * unitX );
486 
487  walkSteps--;
488 
489  if ( state == PlatformCharacterState.Idle )
490  SetAnimation( AnimWalk );
491  }
492  else
493  {
494  // Not walking
495  if ( state == PlatformCharacterState.Idle )
496  SetAnimation( AnimIdle );
497  /*switch ( state )
498  {
499  case PlatformCharacterState.Idle: SetAnimation( AnimIdle ); break;
500  case PlatformCharacterState.Falling: SetAnimation( AnimFall ); break;
501  case PlatformCharacterState.Jumping: SetAnimation( AnimJump ); break;
502  }*/
503  }
504 
505  base.Update(time);
506  }
507 
512  public override void Move( Vector movement )
513  {
514  if ( movement.X > 0 ) Walk( Direction.Right );
515  else if ( movement.X < 0 ) Walk( Direction.Left );
516  if ( movement.Y > 0 ) Jump( movement.Y );
517  }
518 }
double Interval
Aika sekunneissa, jonka välein TimeOut tapahtuu.
Definition: Timer.cs:99
Color Color
Väri, jonka värisenä olio piirretään, jos tekstuuria ei ole määritelty.
override void Update(Time time)
Ajetaan kun pelitilannetta päivitetään. Päivityksen voi toteuttaa omassa luokassa toteuttamalla tämän...
static readonly Vector UnitX
Vaakasuuntainen yksikkövektori (pituus 1, suunta oikealle).
Definition: Vector.cs:66
Kuvio.
Definition: Shapes.cs:48
Animation AnimWalk
Kävelyanimaatio (oikealle)
void PlayAnimation(Animation anim, Action onPlayed=null)
void Turn(Direction direction)
Kääntyy.
double Magnitude
Vektorin pituus.
Definition: Vector.cs:281
PhysicsObject Platform
Fysiikkaolio jonka päällä seisotaan.
Angle Angle
Kulma radiaaneina.
Definition: Vector.cs:308
double X
Olion paikan X-koordinaatti.
void Stop()
Pysäyttää ajastimen ja nollaa sen tilan.
Definition: Timer.cs:255
Action Timeout
Tapahtuu väliajoin.
Definition: Timer.cs:46
bool WalkOnAir
Toistetaanko kävelyanimaatiota ilmassa liikuttaessa?
virtual void Push(Vector force)
Työntää oliota.
Definition: Movement.cs:127
Action Played
Tapahtuma, joka tapahtuu kun animaatio on suoritettu.
Definition: Animation.cs:174
override Angle Angle
Olion kulma tai rintamasuunta. Nolla = osoittaa oikealle.
PhysicsObject(double width, double height)
Luo uuden fysiikkaolion.
override void Move(Vector movement)
Siirtää oliota.
Animation AnimFall
Putoamisanimaatio (oikealle)
static readonly Color Red
Punainen.
Definition: Color.cs:804
GameObject(double width, double height)
Alustaa uuden peliolion.
bool CanMoveOnAir
Jos false, hahmoa ei voi liikuttaa kun se on ilmassa.
Suuntakulma (rajoitettu -180 ja 180 asteen välille) asteina ja radiaaneina. Tietoja kulmasta: http://...
Definition: Angle.cs:40
double LinearDamping
Olion hidastuminen. Hidastaa olion vauhtia, vaikka se ei osuisi mihinkään. Vähän kuin väliaineen (esi...
Definition: Inertia.cs:196
void Jump(double speed)
Hyppää tietyllä nopeudella, jos hahmo seisoo tukevalla pohjalla.
bool IgnoresCollisionResponse
Jättääkö olio törmäyksen huomioimatta. Jos tosi, törmäyksestä tulee tapahtuma, mutta itse törmäystä e...
Definition: Collisions.cs:72
PlatformCharacter2(double width, double height, Shape shape)
Luo uuden tasohyppelyhahmon.
Animation AnimJump
Hyppyanimaatio (oikealle)
static Direction Left
Suunta vasemmalle.
Definition: Direction.cs:70
double MaxVelocity
Hahmon maksiminopeus.
void StopWalking()
Pysähtyy.
Vector Velocity
Olion nopeus.
Definition: Movement.cs:46
static readonly Rectangle Rectangle
Suorakulmio.
Definition: Shapes.cs:72
bool IgnoresGravity
Jättääkö olio painovoiman huomioimatta.
bool CanRotate
Jos false, olio ei voi pyöriä.
Definition: Inertia.cs:45
void Remove(IGameObject childObject)
Poistaa lapsiolion. Jos haluat tuhota olion, kutsu mielummin olion Destroy-metodia.
double Acceleration
Hahmon kiihtyvyys.
Peliolio, joka noudattaa fysiikkamoottorin määräämiä fysiikan lakeja. Voidaan kuitenkin myös laittaa ...
Definition: Coefficients.cs:36
static Game Instance
Definition: Game.cs:149
TimeSpan SinceLastUpdate
Aika joka on kulunut viime päivityksestä.
Definition: Time.cs:24
Vector Gravity
Painovoima. Voimavektori, joka vaikuttaa kaikkiin ei-staattisiin kappaleisiin.
Definition: PhysicsGame.cs:56
static readonly Vector Zero
Nollavektori.
Definition: Vector.cs:61
double Mass
Olion massa. Mitä suurempi massa, sitä suurempi voima tarvitaan olion liikuttamiseksi.
Definition: Inertia.cs:69
PlatformCharacter2(double width, double height)
Luo uuden tasohyppelyhahmon.
Sisältää tiedon ajasta, joka on kulunut pelin alusta ja viime päivityksestä.
Definition: Time.cs:13
bool IsUpdated
Tarvitseeko olio päivittämistä. Kun perit oman luokkasi tästä luokasta, aseta tämä arvoon true...
void Add(IGameObject o)
Lisää olion peliin. Tavalliset oliot tulevat automaattisesti kerrokselle 0 ja ruutuoliot päällimmäise...
Definition: Game.cs:691
static readonly Angle StraightAngle
Oikokulma (180 astetta).
Definition: Angle.cs:55
Perussuunta tasossa.
Definition: Direction.cs:50
virtual void Hit(Vector impulse)
Kohdistaa kappaleeseen impulssin. Tällä kappaleen saa nopeasti liikkeeseen.
Definition: Movement.cs:147
double Y
Definition: Vector.cs:275
double Restitution
Olion kimmoisuus. Arvo välillä 0.0-1.0. Arvolla 1.0 olio säilyttää kaiken vauhtinsa törmäyksessä...
Definition: Coefficients.cs:66
Peliluokka reaaliaikaisille peleille.
Definition: DebugScreen.cs:10
Action< Direction > DirectionChanged
Tapahtuu kun suunta vaihtuu.
void Start()
Käynnistää animaation alusta.
Definition: Animation.cs:282
Vector LeftNormal
Vasen normaali.
Definition: Vector.cs:82
void ForceJump(double speed)
Hyppää vaikka olio ei olisikaan toisen päällä.
static readonly Angle Zero
Nollakulma.
Definition: Angle.cs:45
static double DotProduct(Vector left, Vector right)
Pistetulo.
Definition: Vector.cs:140
int FrameCount
Ruutujen määrä.
Definition: Animation.cs:82
Vector GetVector()
Definition: Direction.cs:116
double X
Definition: Vector.cs:274
double Height
Kentän korkeus.
Definition: Level.cs:126
double Radians
Palauttaa tai asettaa kulman radiaaneina.
Definition: Angle.cs:86
Level Level
Aktiivinen kenttä.
Definition: Game.cs:299
Tasohyppelypelin hahmo. Voi liikkua ja hyppiä. Lisäksi sillä voi olla ase.
override void PrepareThrowable(PhysicsObject obj, Angle angle, double force, double distanceDelta, double axialDelta)
Action AddedToGame
Tapahtuu, kun olio lisätään peliin.
Vector TextureWrapSize
Määrittää kuinka moneen kertaan kuva piirretään. Esimerkiksi (3.0, 2.0) piirtää kuvan 3 kertaa vaakas...
static readonly Color Blue
Sininen.
Definition: Color.cs:504
Ajastin, joka voidaan asettaa laukaisemaan tapahtumia tietyin väliajoin.
Definition: Timer.cs:39
Body Body
Fysiikkamoottorin käyttämä tietorakenne.
Definition: Dimensions.cs:42
Direction FacingDirection
Hahmon rintamasuunta (vasen tai oikea).
Väri.
Definition: Color.cs:13
Kantaluokka fysiikkapeleille.
double Width
Kentän leveys.
Definition: Level.cs:117
void Walk()
Kävelee.
Vector PlatformNormal
Suuntavektori, joka osoittaa tason suuntaan. Nollavektori, jos ilmassa.
static Angle Supplement(Angle a)
Laskee suplementtikulman (180 asteen kulman toinen puoli)
Definition: Angle.cs:366
Sarja kuvia, jotka vaihtuvat halutulla nopeudella. Yksi animaatio koostuu yhdestä tai useammasta kuva...
Definition: Animation.cs:56
override Vector Position
Olion paikka koordinaatistossa. Käsittää sekä X- että Y-koordinaatin.
Definition: Dimensions.cs:49
void Add(IGameObject childObject)
Lisää annetun peliolion tämän olion lapseksi. Lapsiolio liikkuu tämän olion mukana, ja sen paikka ja koko ilmaistaan suhteessa tähän olioon.
double KineticFriction
Liikekitka. Liikettä vastustava voima joka ilmenee kun kaksi oliota liikkuu toisiaan vasten (esim...
Definition: Coefficients.cs:55
void Start()
Käynnistää ajastimen.
Definition: Timer.cs:220
static Direction Right
Suunta oikealle.
Definition: Direction.cs:75
double Width
Olion leveys (X-suunnassa, leveimmässä kohdassa).
int PlatformTolerance
Toleranssiarvo, joka määrittää kuinka monta pelin päivitysjaksoa hahmo voi olla irti tasosta ennen ku...
bool LoopJumpAnim
Toistetaanko hyppyanimaatiota useammin kuin kerran.
2D-vektori.
Definition: Vector.cs:56
void Walk(Direction direction)
Kävelee tiettyyn suuntaan.
Peli, jossa on fysiikan laskenta mukana. Peliin lisätyt
Definition: PhysicsGame.cs:45
Pelialueella liikkuva olio. Käytä fysiikkapeleissä PhysicsObject-olioita.
Definition: __GameObject.cs:54
Vector AbsolutePosition
Olion absoluuttinen paikka pelimaailmassa. Jos olio ei ole minkään toisen peliolion lapsiolio...
Animation AnimIdle
Animaatio, jota käytetään kun hahmo on paikallaan (kääntyneenä oikealle)
virtual Vector Position
Olion paikka. Jos olio on jonkun toisen peliolion lapsi, paikka on suhteessa tämän vanhempaan (Parent...
bool LoopFallAnim
Toistetaanko putoamisanimaatiota useammin kuin kerran.
object Tag
Vapaasti asetettava muuttuja.
double Height
Olion korkeus (Y-suunnassa, korkeimmassa kohdassa).