source: 2014/24/EemeliK/Zombieland/Jypeli/GameObjects/PhysicsStructure.cs @ 5974

Revision 5974, 21.7 KB checked in by empaheik, 4 years ago (diff)
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using Physics2DDotNet;
6using Physics2DDotNet.Ignorers;
7using AdvanceMath;
8using Jypeli.Controls;
9
10namespace Jypeli
11{
12    /// <summary>
13    /// Rakenne, joka pitää fysiikkaoliot kiinteän matkan päässä toisistaan.
14    /// </summary>
15    public class PhysicsStructure : GameObjects.GameObjectBase, IPhysicsObjectInternal, GameObjectContainer
16    {
17        private double _softness = 0;
18        private List<PhysicsObject> objects;
19
20        /// <summary>
21        /// Onko rakenne lisätty peliin.
22        /// </summary>
23        public bool IsAddedToGame { get; set; }
24
25        /// <summary>
26        /// Rakenteeseen kuuluvat oliot.
27        /// </summary>
28        public IList<PhysicsObject> Objects
29        {
30            get { return objects.AsReadOnly(); }
31        }
32
33        /// <summary>
34        /// Rakenteeseen kuuluvat liitokset.
35        /// </summary>
36        internal List<AxleJoint> Joints { get; private set; }
37
38        /// <summary>
39        /// Olioiden välisten liitosten pehmeys.
40        /// </summary>
41        public double Softness
42        {
43            get { return _softness; }
44            set
45            {
46                _softness = value;
47
48                for ( int i = 0; i < Joints.Count; i++ )
49                {
50                    Joints[i].Softness = value;
51                }
52            }
53        }
54
55        public BoundingRectangle BoundingRectangle
56        {
57            get
58            {
59                if ( objects.Count == 0 )
60                    return new BoundingRectangle();
61
62                double top = objects[0].Top;
63                double left = objects[0].Left;
64                double bottom = objects[0].Bottom;
65                double right = objects[0].Right;
66
67                for ( int i = 1; i < objects.Count; i++ )
68                {
69                    if ( objects[i].Top > top ) top = objects[i].Top;
70                    if ( objects[i].Left < left ) left = objects[i].Left;
71                    if ( objects[i].Bottom < bottom ) bottom = objects[i].Bottom;
72                    if ( objects[i].Right > right ) right = objects[i].Right;
73                }
74
75                return new BoundingRectangle( new Vector( left, top ), new Vector( right, bottom ) );
76            }
77        }
78
79        #region Tagged
80       
81        public object Tag { get; set; }
82
83        #endregion
84
85        #region IGameObject
86
87        public event Action AddedToGame;
88
89        bool _isVisible = true;
90        bool _ignoresLighting = false;
91        PhysicsObject centerObject;
92
93        public bool IsVisible
94        {
95            get { return false; }
96            set
97            {
98                foreach ( var obj in objects )
99                {
100                    obj.IsVisible = value;
101                }
102
103                _isVisible = value;
104            }
105        }
106
107        /// <summary>
108        /// Jättääkö olio kentän valaistuksen huomiotta.
109        /// </summary>
110        public bool IgnoresLighting
111        {
112            get { return _ignoresLighting; }
113            set
114            {
115                objects.ForEach( o => o.IgnoresLighting = value );
116                _ignoresLighting = value;
117            }
118        }
119
120        public List<Listener> AssociatedListeners { get; private set; }
121
122        /// <summary>
123        /// Rakenteen paikka pelimaailmassa.
124        /// </summary>
125        public override Vector Position
126        {
127            get
128            {
129                return centerObject.Position;
130            }
131            set
132            {
133                Vector delta = value - Position;
134
135                foreach ( var obj in objects )
136                {
137                    obj.Position += delta;
138                }
139
140                centerObject.Position = value;
141            }
142        }
143
144        public override Vector Size
145        {
146            get
147            {
148                return BoundingRectangle.Size;
149            }
150            set
151            {
152                throw new NotImplementedException( "Setting size for a structure is not implemented." );
153            }
154        }
155
156        public override Animation Animation
157        {
158            get { return null; }
159            set
160            {
161                throw new InvalidOperationException( "Physics structure has no image or animation." );
162            }
163        }
164
165        public Vector TextureWrapSize
166        {
167            get { return new Vector( 1, 1 ); }
168            set { throw new NotImplementedException(); }
169        }
170
171        public bool TextureFillsShape
172        {
173            get { return false; }
174            set { throw new NotImplementedException(); }
175        }
176
177        public Color Color
178        {
179            get
180            {
181                throw new NotImplementedException();
182            }
183            set
184            {
185                foreach ( var obj in objects )
186                    obj.Color = value;
187            }
188        }
189
190        public Shape Shape
191        {
192            get { return Shape.Rectangle; }
193            set { throw new NotImplementedException(); }
194        }
195
196        public override Angle Angle
197        {
198            // TODO: Rotation
199            get { return Angle.Zero; }
200            set { throw new NotImplementedException(); }
201        }
202
203        #endregion
204
205        #region IPhysicsObject
206
207        bool _ignoresGravity = false;
208        bool _ignoresCollisionResponse = false;
209        bool _ignoresExplosions = false;
210        bool _ignoresPhysicsLogics = false;
211        Ignorer _collisionIgnorer = null;
212        int _collisionIgnoreGroup = 0;
213        Coefficients _coeffs = PhysicsObject.DefaultCoefficients.Duplicate();
214        double _linearDamping = 1;
215        double _angularDamping = 1;
216        double? _setMomentOfInertia = null;
217        double _calcMomentOfInertia = 0;
218
219        /// <summary>
220        /// Tapahtuu kun olio törmää toiseen.
221        /// </summary>
222        public event CollisionHandler<IPhysicsObject, IPhysicsObject> Collided;
223
224        /// <summary>
225        /// Rakenneolio, johon tämä olio kuuluu.
226        /// </summary>
227        public PhysicsStructure ParentStructure
228        {
229            // No nested physics structures for now
230            get { return null; }
231        }
232
233        public double Mass
234        {
235            get
236            {
237                double totalMass = 0;
238                foreach ( var part in objects )
239                    totalMass += part.Mass;
240                return totalMass;
241            }
242            set
243            {
244                throw new NotImplementedException( "Setting mass for a structure is not implemented." );
245            }
246        }
247
248        public Body Body
249        {
250            get
251            {
252                throw new InvalidOperationException( "Structure has no body." );
253            }
254        }
255
256        public bool IgnoresGravity
257        {
258            get { return _ignoresGravity; }
259            set
260            {
261                foreach ( var obj in objects )
262                    obj.IgnoresGravity = value;
263
264                _ignoresGravity = value;
265            }
266        }
267
268        public bool IgnoresCollisionResponse
269        {
270            get { return _ignoresCollisionResponse; }
271            set
272            {
273                foreach ( var obj in objects )
274                    obj.IgnoresCollisionResponse = value;
275
276                _ignoresCollisionResponse = value;
277            }
278        }
279
280        public bool IgnoresExplosions
281        {
282            get { return _ignoresExplosions; }
283            set
284            {
285                foreach ( var obj in objects )
286                    obj.IgnoresExplosions = value;
287
288                _ignoresExplosions = value;
289            }
290        }
291
292        public bool IgnoresPhysicsLogics
293        {
294            get { return _ignoresPhysicsLogics; }
295            set
296            {
297                foreach ( var obj in objects )
298                    obj.IgnoresPhysicsLogics = value;
299
300                _ignoresPhysicsLogics = value;
301            }
302        }
303
304        public Ignorer CollisionIgnorer
305        {
306            get { return _collisionIgnorer; }
307            set
308            {
309                foreach ( var obj in objects )
310                    obj.CollisionIgnorer = value;
311
312                _collisionIgnorer = value;
313            }
314        }
315
316        public int CollisionIgnoreGroup
317        {
318            get { return _collisionIgnoreGroup; }
319            set
320            {
321                foreach ( var obj in objects )
322                    obj.CollisionIgnoreGroup = value;
323
324                _collisionIgnoreGroup = value;
325            }
326        }
327
328        public double StaticFriction
329        {
330            get { return _coeffs.StaticFriction; }
331            set
332            {
333                foreach ( var obj in objects )
334                    obj.StaticFriction = value;
335
336                _coeffs.StaticFriction = value;
337            }
338        }
339
340        public double KineticFriction
341        {
342            get { return _coeffs.DynamicFriction; }
343            set
344            {
345                foreach ( var obj in objects )
346                    obj.KineticFriction = value;
347
348                _coeffs.DynamicFriction = value;
349            }
350        }
351
352        public double Restitution
353        {
354            get { return _coeffs.Restitution; }
355            set
356            {
357                foreach ( var obj in objects )
358                    obj.Restitution = value;
359
360                _coeffs.Restitution = value;
361            }
362        }
363
364        public double LinearDamping
365        {
366            get { return _linearDamping; }
367            set
368            {
369                foreach ( var obj in objects )
370                    obj.LinearDamping = value;
371
372                _linearDamping = value;
373            }
374        }
375
376        public double AngularDamping
377        {
378            get { return _angularDamping; }
379            set
380            {
381                foreach ( var obj in objects )
382                    obj.AngularDamping = value;
383
384                _angularDamping = value;
385            }
386        }
387
388        public Vector Velocity
389        {
390            get
391            {
392                var velocities = objects.ConvertAll<PhysicsObject, Vector>( delegate( PhysicsObject o ) { return o.Velocity; } );
393                return Vector.Average( velocities );
394            }
395            set
396            {
397                foreach ( var obj in objects )
398                    obj.Velocity = value;
399            }
400        }
401
402        public double AngularVelocity
403        {
404            get
405            {
406                IEnumerable<double> velocities = objects.ConvertAll<PhysicsObject, double>( delegate( PhysicsObject o ) { return o.AngularVelocity; } );
407                return velocities.Average();
408            }
409            set
410            {
411                foreach ( var obj in objects )
412                    obj.AngularVelocity = value;
413            }
414        }
415
416        public Vector Acceleration
417        {
418            get
419            {
420                var accs = objects.ConvertAll<PhysicsObject, Vector>( delegate( PhysicsObject o ) { return o.Acceleration; } );
421                return Vector.Average( accs );
422            }
423            set
424            {
425                foreach ( var obj in objects )
426                    obj.Acceleration = value;
427            }
428        }
429
430        public double AngularAcceleration
431        {
432            get
433            {
434                IEnumerable<double> accs = objects.ConvertAll<PhysicsObject, double>( delegate( PhysicsObject o ) { return o.AngularAcceleration; } );
435                return accs.Average();
436            }
437            set
438            {
439                foreach ( var obj in objects )
440                    obj.AngularAcceleration = value;
441            }
442        }
443
444        public double MomentOfInertia
445        {
446            get
447            {
448                return _setMomentOfInertia.HasValue ? _setMomentOfInertia.Value : _calcMomentOfInertia;
449            }
450            set
451            {
452                _setMomentOfInertia = value;
453            }
454        }
455
456        /// <summary>
457        /// Jos <c>false</c>, olio ei voi pyöriä.
458        /// </summary>
459        public bool CanRotate
460        {
461            get { return !double.IsPositiveInfinity( MomentOfInertia ); }
462            set
463            {
464                if ( !value )
465                {
466                    MomentOfInertia = double.PositiveInfinity;
467                }
468                else
469                {
470                    CalculateMomentOfInertia();
471                    _setMomentOfInertia = null;
472                }
473            }
474        }
475
476        #endregion
477
478        /// <summary>
479        /// Luo uuden tyhjän rakenteen.
480        /// </summary>
481        public PhysicsStructure()
482        {
483            centerObject = new PhysicsObject( 1, 1 ) { IgnoresPhysicsLogics = true, IsVisible = false };
484            objects = new List<PhysicsObject>();
485            Joints = new List<AxleJoint>();
486            AssociatedListeners = new List<Listener>();
487            AddedToGame += AddJoints;
488            Removed += RemoveJoints;
489        }
490
491        private void AddJoints()
492        {
493            Joints.ForEach( ( (PhysicsGameBase)Game.Instance ).Add );
494        }
495
496        private void RemoveJoints()
497        {
498            Joints.ForEach( ( (PhysicsGameBase)Game.Instance ).Remove );
499        }
500
501        /// <summary>
502        /// Luo uuden rakenteen ja varustaa sen fysiikkaolioilla.
503        /// </summary>
504        /// <param name="objs">Fysiikkaoliot</param>
505        public PhysicsStructure( params PhysicsObject[] objs )
506            : this()
507        {
508            for ( int i = 0; i < objs.Length; i++ )
509            {
510                Add( objs[i] );
511            }
512        }
513
514        /// <summary>
515        /// Kutsutaan kun olio lisätään peliin.
516        /// </summary>
517        public void OnAddedToGame()
518        {
519            if ( AddedToGame != null )
520                AddedToGame();
521            //brain.AddToGameEvent();
522        }
523
524        /// <summary>
525        /// Kutsutaan kun törmätään.
526        /// </summary>
527        internal void OnCollided( IPhysicsObject part, IPhysicsObject target )
528        {
529            if ( Collided != null )
530                Collided( this, target );
531        }
532
533        public void Update( Time time )
534        {
535            foreach ( var obj in objects )
536            {
537                if ( obj.IsUpdated )
538                    obj.Update( time );
539            }
540        }
541
542        /// <summary>
543        /// Lisää olion rakenteeseen.
544        /// </summary>
545        /// <param name="obj">Lisättävä olio</param>
546        public void Add( IGameObject obj )
547        {
548            if ( !( obj is PhysicsObject ) )
549                throw new NotImplementedException( "Currently only PhysicsObjects can be added to a structure." );
550
551            if ( !IsAddedToGame )
552            {
553                // Add to game and use relative coordinates
554                obj.Position += this.Position;
555
556                if ( !obj.IsAddedToGame )
557                    Game.Instance.Add( obj );
558            }
559
560            PhysicsObject physObj = (PhysicsObject)obj;
561            physObj.ParentStructure = this;
562            physObj.IsVisible = _isVisible;
563            physObj.IgnoresGravity = _ignoresGravity;
564            physObj.IgnoresCollisionResponse = _ignoresCollisionResponse;
565            physObj.IgnoresExplosions = _ignoresExplosions;
566            physObj.IgnoresPhysicsLogics = _ignoresPhysicsLogics;
567            physObj.CollisionIgnorer = _collisionIgnorer;
568            physObj.CollisionIgnoreGroup = _collisionIgnoreGroup;
569            physObj.Restitution = _coeffs.Restitution;
570            physObj.StaticFriction = _coeffs.StaticFriction;
571            physObj.KineticFriction = _coeffs.DynamicFriction;
572            physObj.LinearDamping = _linearDamping;
573            physObj.AngularDamping = _angularDamping;
574
575            physObj.Collided += this.OnCollided;
576
577            foreach ( var existing in objects )
578            {
579                AddJoint( physObj, existing );
580            }
581
582            objects.Add( physObj );
583            CalculateMomentOfInertia();
584        }
585
586        private void AddJoint( PhysicsObject obj1, PhysicsObject obj2 )
587        {
588            AxleJoint joint = new AxleJoint( obj1, obj2 );
589            joint.Softness = _softness;
590            Joints.Add( joint );
591            ( (PhysicsGameBase)Game.Instance ).Add( joint );
592        }
593
594        public void Remove( IGameObject obj )
595        {
596            if ( !( obj is PhysicsObject ) )
597                throw new NotImplementedException( "Currently only PhysicsObjects can be added to a structure." );
598
599            PhysicsObject physObj = (PhysicsObject)obj;
600
601            if ( !objects.Contains( physObj ) )
602                return;
603
604            physObj.ParentStructure = null;
605            physObj.CollisionIgnorer = null;
606            physObj.CollisionIgnoreGroup = 0;
607            physObj.Collided -= this.OnCollided;
608
609            foreach ( var joint in Joints.FindAll( j => j.Object1 == physObj || j.Object2 == physObj ) )
610            {
611                ( (PhysicsGameBase)Game.Instance ).Remove( joint );
612            }
613
614            objects.Remove( physObj );
615            CalculateMomentOfInertia();
616        }
617
618        private void CalculateMomentOfInertia()
619        {
620            Vector center = this.Position;
621            _calcMomentOfInertia = 0;
622
623            foreach ( var part in objects )
624            {
625                double r = Vector.Distance( center, part.Position );
626                _calcMomentOfInertia += part.Mass * r * r;
627            }
628        }
629
630        public bool IsInside( Vector point )
631        {
632            foreach ( var obj in objects )
633            {
634                if ( obj.IsInside( point ) )
635                    return true;
636            }
637
638            return false;
639        }
640
641        #region IPhysicsObject
642
643        public void Hit( Vector impulse )
644        {
645            Vector velocity = impulse / Mass;
646
647            foreach ( var obj in objects )
648            {
649                obj.Hit( velocity * obj.Mass );
650            }
651        }
652
653        public void Push( Vector force )
654        {
655            Vector acceleration = force / Mass;
656
657            foreach ( var obj in objects )
658            {
659                obj.Push( acceleration * obj.Mass );
660            }
661        }
662
663        public void Push( Vector force, TimeSpan time )
664        {
665            Vector acceleration = force / Mass;
666
667            foreach ( var obj in objects )
668            {
669                obj.Push( acceleration * obj.Mass, time );
670            }
671        }
672
673        public void ApplyTorque( double torque )
674        {
675            if ( MomentOfInertia == 0 || double.IsInfinity( MomentOfInertia ) )
676                return;
677
678            double angularAcc = torque / MomentOfInertia;
679            Vector center = this.Position;
680
681            foreach ( var obj in objects )
682            {
683                Vector radius = obj.Position - center;
684                double linearAcc = radius.Magnitude * angularAcc;
685                obj.Push( linearAcc * obj.Mass * radius.LeftNormal );
686            }
687        }
688
689        public void Move( Vector movement )
690        {
691            foreach ( var obj in objects )
692            {
693                obj.Move( movement );
694            }
695        }
696
697        public override void MoveTo( Vector location, double speed, Action doWhenArrived )
698        {
699            centerObject.MoveTo( location, speed, doWhenArrived );
700
701            foreach ( var obj in objects )
702            {
703                Vector displacement = obj.AbsolutePosition - centerObject.AbsolutePosition;
704                obj.MoveTo( location + displacement, speed );
705            }
706        }
707
708        public void StopMoveTo()
709        {
710            foreach ( var obj in objects )
711                obj.StopMoveTo();
712        }
713
714        public void Stop()
715        {
716            foreach ( var obj in objects )
717                obj.Stop();
718        }
719
720        public void StopHorizontal()
721        {
722            foreach ( var obj in objects )
723                obj.StopHorizontal();
724        }
725
726        public void StopVertical()
727        {
728            foreach ( var obj in objects )
729                obj.StopVertical();
730        }
731
732        #endregion
733
734        #region DelayedDestroyable
735
736        #region DelayedDestroyable
737
738        /// <summary>
739        /// Onko olio tuhoutumassa.
740        /// </summary>
741        public bool IsDestroying { get; private set; }
742
743        /// <summary>
744        /// Tapahtuu, kun olion tuhoaminen alkaa.
745        /// </summary>
746        public event Action Destroying;
747
748        protected void OnDestroying()
749        {
750            if ( Destroying != null )
751                Destroying();
752        }
753
754        public override void Destroy()
755        {
756            IsDestroying = true;
757            OnDestroying();
758            Game.DoNextUpdate( ReallyDestroy );
759        }
760
761        protected virtual void ReallyDestroy()
762        {
763            foreach ( var joint in Joints ) joint.Destroy();
764            foreach ( var obj in objects ) obj.Destroy();
765            this.MaximumLifetime = new TimeSpan( 0 );
766            base.Destroy();
767        }
768
769        #endregion
770
771        #endregion
772
773        #region Unimplemented Members / IGameObject
774
775        public IGameObject Parent
776        {
777            get { throw new NotImplementedException(); }
778            set { throw new NotImplementedException(); }
779        }
780
781        #endregion
782    }
783}
Note: See TracBrowser for help on using the repository browser.