source: 2014/30/MiskaK/The Reclaim/The Reclaim/Jypeli/Camera.cs @ 5602

Revision 5480, 15.9 KB checked in by anlakane, 6 years ago (diff)

Lisätty Reclaim versionhallintaan

Line 
1#region MIT License
2/*
3 * Copyright (c) 2009 University of Jyväskylä, Department of Mathematical
4 * Information Technology.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#endregion
25
26/*
27 * Authors: Tero Jäntti, Tomi Karppinen, Janne Nikkanen.
28 */
29
30using System;
31using Microsoft.Xna.Framework;
32using AdvanceMath;
33using Microsoft.Xna.Framework.Graphics;
34using System.Collections.Generic;
35
36namespace Jypeli
37{
38    /// <summary>
39    /// Kamera. Määrittää mikä osa pelitasosta on kerralla näkyvissä.
40    /// </summary>
41    [Save]
42    public class Camera
43    {
44        // Huom. tätä käytetään vain jos seurataan useita olioita kerralla
45        private List<GameObject> followedObjects = null;
46
47        [Save] internal bool _stayInLevel = false;
48        [Save] internal double _zoomFactor = 1.0;
49
50        /// <summary>
51        /// Kameran sijainti.
52        /// </summary>
53        [Save]public Vector Position = Vector.Zero;
54
55        /// <summary>
56        /// Kameran liikkumisnopeus.
57        /// </summary>
58        [Save] public Vector Velocity = Vector.Zero;
59
60        /// <summary>
61        /// Kameran paikan X-koordinaatti.
62        /// </summary>
63        public double X
64        {
65            get
66            {
67                return Position.X;
68            }
69            set
70            {
71                Position = new Vector(value, Position.Y);
72            }
73        }
74
75        /// <summary>
76        /// Kameran paikan Y-koordinaatti.
77        /// </summary>
78        public double Y
79        {
80            get
81            {
82                return Position.Y;
83            }
84            set
85            {
86                Position = new Vector(Position.X, value);
87            }
88        }
89
90        /// <summary>
91        /// Kameran zoomauskerroin.
92        /// Oletuksena 1.0. Mitä suurempi zoomauskerroin, sitä lähempänä kamera on (esim 2.0 on 2 x lähempänä) ja toisinpäin.
93        /// </summary>
94        public double ZoomFactor
95        {
96            get { return _zoomFactor; }
97            set { _zoomFactor = value; }
98        }
99
100        /// <summary>
101        /// Jos tosi, kamera ei mene koskaan kentän ulkopuolelle.
102        /// </summary>
103        public bool StayInLevel
104        {
105            get { return _stayInLevel; }
106            set { _stayInLevel = value; }
107        }
108
109        /// <summary>
110        /// Olio, jota kamera seuraa. Jos <c>null</c>, mitään oliota ei seurata.
111        /// </summary>
112        public GameObject FollowedObject { get; set; }
113
114        /// <summary>
115        /// Seurataanko oliota (FollowedObject) x- eli vaakasuunnassa.
116        /// </summary>
117        public bool FollowsX { get; set; }
118
119        /// <summary>
120        /// Seurataanko oliota (FollowedObject) y- eli pystysuunnassa.
121        /// </summary>
122        public bool FollowsY { get; set; }
123
124        /// <summary>
125        /// Jos kamera seuraa oliota, tällä voi säätää missä kohtaa ruutua pelaaja näkyy.
126        /// Toisin sanoen ruutukoordinaateissa kerrotaan, kuinka kaukana ruudun keskustasta
127        /// seurattava olio näkyy.
128        /// </summary>
129        /// <example>
130        /// Pelaajan näyttäminen ruudun alareunassa, vaakasuunnassa keskellä:
131        /// <code>
132        /// Camera.Follow( pelaaja );
133        /// Camera.FollowOffset = new Vector( 0, Screen.Height * 0.4 );
134        /// </code>
135        /// </example>
136        [Save] public Vector FollowOffset { get; set; }
137
138        /// <summary>
139        /// Jos kamera seuraa useita olioita, tällä voi säätää kuinka paljon vasempaan ja
140        /// oikeaan reunaan jätetään tyhjää tilaa.
141        /// </summary>
142        public double FollowXMargin { get; set; }
143
144        /// <summary>
145        /// Jos kamera seuraa useita olioita, tällä voi säätää kuinka paljon ylä- ja
146        /// alareunaan jätetään tyhjää tilaa.
147        /// </summary>
148        public double FollowYMargin { get; set; }
149
150        /// <summary>
151        /// Luo uuden kameran.
152        /// </summary>
153        internal Camera()
154        {
155            FollowsX = true;
156            FollowsY = true;
157            FollowXMargin = FollowYMargin = 600;
158        }
159
160        /// <summary>
161        /// Muuntaa annetun pisteen ruutukoordinaateista maailmankoordinaatteihin.
162        /// </summary>
163        public Vector ScreenToWorld(Vector point)
164        {
165            return Position + (1 / ZoomFactor) * point;
166        }
167
168        /// <summary>
169        /// Muuntaa annetun pisteen maailmankoordinaateista ruutukoordinaatteihin.
170        /// </summary>
171        public Vector WorldToScreen(Vector point)
172        {
173            return (point - Position) * ZoomFactor;
174        }
175
176        /// <summary>
177        /// Muuntaa annetun pisteen ruutukoordinaateista maailmankoordinaatteihin
178        /// ottaen huomioon oliokerroksen suhteellisen siirtymän.
179        /// </summary>
180        public Vector ScreenToWorld( Vector point, Layer layer )
181        {
182            if ( layer == null )
183                return ScreenToWorld( point );
184            if ( layer.IgnoresZoom )
185                return Vector.ComponentProduct( Position, layer.RelativeTransition ) + point;
186
187            return Vector.ComponentProduct( Position, layer.RelativeTransition ) + ( 1 / ZoomFactor ) * point;
188        }
189
190        /// <summary>
191        /// Kohdistaa kameran.
192        /// </summary>
193        /// <param name="v">Koordinaatti vektorina johon kamera kohdistetaan.</param>
194        public void Move( Vector v )
195        {
196            Position += new Vector( v.X / ZoomFactor, v.Y / ZoomFactor );
197        }
198
199        /// <summary>
200        /// Zoomaa.
201        /// </summary>
202        /// <param name="zoom">
203        /// Zoomauskerroin. Ykköstä suurempi (esim. 1.5) lähentää ja
204        /// ykköstä pienempi (esim. 0.5) zoomaa kauemmas.
205        /// </param>
206        public void Zoom( double zoom )
207        {
208            ZoomFactor *= zoom;
209        }
210
211        /// <summary>
212        /// Resetoi kameran (keskittää, laittaa zoomin oletusarvoon ja lopettaa seuraamisen).
213        /// </summary>
214        public void Reset()
215        {
216            Position = Vector.Zero;
217            Velocity = Vector.Zero;
218            ZoomFactor = 1.0f;
219            StopFollowing();
220        }
221
222        /// <summary>
223        /// Seuraa yhtä tai useampaa peliobjektia.
224        /// </summary>
225        /// <param name="gameobjects">Seurattavat peliobjektit.</param>
226        public void Follow( params GameObject[] gameobjects )
227        {
228            FollowsX = true;
229            FollowsY = true;
230
231            if ( gameobjects.Length == 0 ) return;
232            if ( gameobjects.Length == 1 )
233            {
234                FollowedObject = gameobjects[0];
235                return;
236            }
237
238            FollowedObject = new GameObject( 1.0, 1.0 );
239            followedObjects = new List<GameObject>( gameobjects );
240            updateAvgPoint();
241        }
242
243        /// <summary>
244        /// Seuraa jotakin peliobjektia X- eli vaakasuunnassa.
245        /// </summary>
246        /// <param name="gameobjects">Seurattavat peliobjektit.</param>
247        public void FollowX( params GameObject[] gameobjects )
248        {
249            Follow( gameobjects );
250            FollowsX = true;
251            FollowsY = false;
252        }
253
254        /// <summary>
255        /// Seuraa jotakin peliobjektia Y- eli pystysuunnassa.
256        /// </summary>
257        /// <param name="gameobjects">Seurattavat peliobjektit.</param>
258        public void FollowY( params GameObject[] gameobjects )
259        {
260            Follow( gameobjects );
261            FollowsX = false;
262            FollowsY = true;
263        }
264
265        /// <summary>
266        /// Lopettaa olio(iden) seuraamisen.
267        /// </summary>
268        public void StopFollowing()
269        {
270            if ( followedObjects != null )
271            {
272                followedObjects = null;
273                FollowedObject.Destroy();
274            }
275
276            FollowedObject = null;
277        }
278
279        private void updateAvgPoint()
280        {
281            FollowedObject.Position = followedObjects.ConvertAll<GameObject, Vector>( ( GameObject o ) => { return o.Position; } ).Average();
282
283            double maxDx = followedObjects.ConvertAll<GameObject, double>( ( GameObject o ) => { return Math.Abs( o.X - FollowedObject.X ); } ).Max();
284            double maxDy = followedObjects.ConvertAll<GameObject, double>( ( GameObject o ) => { return Math.Abs( o.Y - FollowedObject.Y ); } ).Max();
285
286            double zoomX = Game.Screen.Width / ( 2 * maxDx + FollowXMargin );
287            double zoomY = Game.Screen.Height / ( 2 * maxDy + FollowYMargin );
288
289            ZoomFactor = Math.Min( zoomX, zoomY );
290        }
291
292        /// <summary>
293        /// Zoomaa ja sijoittaa kameran niin, että parametreina annettua alue näkyy kokonaan ruudulla.
294        /// </summary>
295        /// <param name="bottomLeft">Alueen vasen alanurkka.</param>
296        /// <param name="topRight">Alueen oikea ylänurkka.</param>
297        public void ZoomTo( Vector bottomLeft, Vector topRight )
298        {
299            ZoomTo( bottomLeft.X, bottomLeft.Y, topRight.X, topRight.Y );
300        }
301
302        public void ZoomTo( BoundingRectangle rectangle )
303        {
304            ZoomTo( rectangle.Left, rectangle.Bottom, rectangle.Right, rectangle.Top );
305        }
306
307        public void ZoomTo( BoundingRectangle rectangle, double borderSize )
308        {
309            ZoomTo( rectangle.Left, rectangle.Bottom, rectangle.Right, rectangle.Top, borderSize );
310        }
311
312        public void ZoomToAllObjects()
313        {
314            ZoomToAllObjects( 0 );
315        }
316
317        /// <summary>
318        /// Zoomaa ja sijoittaa kameran siten, että kaikki pelioliot ovat yhtäaikaa näkyvissä.
319        /// </summary>
320        /// <param name="borderSize">Reunalle jätettävä tila (jos negatiivinen, niin osa kentästä jää piiloon).</param>
321        public void ZoomToAllObjects( double borderSize )
322        {
323            // Do the real zoom next update so all objects waiting to be added are added before that
324            Game.DoNextUpdate( doZoomToAllObjects, borderSize );
325        }
326
327        public void doZoomToAllObjects( double borderSize )
328        {
329            if ( Game.Instance.ObjectCount > 0 )
330                ZoomTo( Game.Instance.Level.FindObjectLimits(), borderSize );
331        }
332
333        /// <summary>
334        /// Zoomaa ja sijoittaa kameran niin, että parametreina annettua alue näkyy kokonaan ruudulla.
335        /// </summary>
336        /// <param name="left">Alueen vasemman reunan x-koordinaatti.</param>
337        /// <param name="bottom">Alueen alareunan y-koordinaatti.</param>
338        /// <param name="right">Alueen oikean reunan x-koordinaatti.</param>
339        /// <param name="top">Alueen yläreunan y-koordinaatti.</param>
340        public void ZoomTo( double left, double bottom, double right, double top )
341        {
342            ZoomTo( left, bottom, right, top, 0 );
343        }
344
345        internal void ZoomTo( double left, double bottom, double right, double top, double borderSize )
346        {
347            double screenWidth = (double)Game.Screen.Width;
348            double screenHeight = (double)Game.Screen.Height;
349            double width = right - left;
350            double height = top - bottom;
351
352            Position = new Vector( left + width / 2, bottom + height / 2 );
353
354            if ( ( width / height ) >= ( screenWidth / screenHeight ) )
355                this.ZoomFactor = screenWidth / ( width + borderSize );
356            else
357                this.ZoomFactor = screenHeight / ( height + borderSize );
358        }
359
360        /// <summary>
361        /// Zoomaa ja keskittää kameran siten, että koko kenttä on näkyvissä kerralla.
362        /// </summary>
363        public void ZoomToLevel()
364        {
365            ZoomToLevel( 0 );
366        }
367
368        /// <summary>
369        /// Zoomaa ja keskittää kameran siten, että koko kenttä on näkyvissä kerralla. Tällöin kamera ei seuraa mitään oliota.
370        /// </summary>
371        /// <param name="borderSize">Reunalle jätettävä tila (jos negatiivinen, niin osa kentästä jää piiloon).</param>
372        public void ZoomToLevel( double borderSize )
373        {
374            FollowedObject = null;
375            Level level = Game.Instance.Level;
376            ZoomTo( level.Left, level.Bottom, level.Right, level.Top, borderSize );
377        }
378
379        /// <summary>
380        /// Ajetaan kun pelitilannetta päivitetään.
381        /// </summary>
382        /// <param name="time">Peliaika</param>
383        internal void Update( Time time )
384        {
385            Position += Velocity * time.SinceLastUpdate.TotalSeconds;
386
387            if ( FollowedObject != null )
388            {
389                Vector center = ScreenToWorld( Vector.Zero );
390                Vector worldOffset = ScreenToWorld( FollowOffset );
391
392                // Update the average point if following multiple objects
393                if ( followedObjects != null ) updateAvgPoint();
394
395                if ( FollowsX && FollowsY )
396                    Position = FollowedObject.Position + ( worldOffset - center );
397                else if ( FollowsX )
398                    X = FollowedObject.X + ( worldOffset.X - center.X );
399                else if ( FollowsY )
400                    Y = FollowedObject.Y + ( worldOffset.Y - center.Y );
401            }
402
403            if ( StayInLevel )
404            {
405                double screenWidth = (double)Game.Screen.Width;
406                double screenHeight = (double)Game.Screen.Height;
407                Level level = Game.Instance.Level;
408
409                double screenAspectRatio = screenWidth / screenHeight;
410                double levelAspectRatio = level.Width / level.Height;
411
412                double zoomedWidth = level.Width * ZoomFactor;
413                double zoomedHeight = level.Height * ZoomFactor;
414
415                double viewAreaWidth = screenWidth / ZoomFactor;
416                double viewAreaHeight = screenHeight / ZoomFactor;
417
418                if ( zoomedWidth < screenWidth || zoomedHeight < screenHeight )
419                {
420                    ZoomFactor = Math.Max( screenWidth / level.Width, screenHeight / level.Height );
421                }
422
423                if ( ( Position.X - ( viewAreaWidth / 2 ) ) < level.Left )
424                {
425                    Position.X = level.Left + ( viewAreaWidth / 2 );
426                }
427                else if ( Position.X + ( viewAreaWidth / 2 ) > level.Right )
428                {
429                    Position.X = level.Right - ( viewAreaWidth / 2 );
430                }
431
432                if ( Position.Y - ( viewAreaHeight / 2 ) < level.Bottom )
433                {
434                    Position.Y = level.Bottom + ( viewAreaHeight / 2 );
435                }
436                else if ( Position.Y + ( viewAreaHeight / 2 ) > level.Top )
437                {
438                    Position.Y = level.Top - ( viewAreaHeight / 2 );
439                }
440            }
441        }
442    }
443}
Note: See TracBrowser for help on using the repository browser.