source: 2015/26/MikkoL/JRPG/JRPG/Jypeli/Camera.cs @ 6401

Revision 6401, 16.6 KB checked in by mijoliim, 5 years ago (diff)
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        public bool Freeze { get; set; }
61
62        /// <summary>
63        /// Kameran paikan X-koordinaatti.
64        /// </summary>
65        public double X
66        {
67            get
68            {
69                return Position.X;
70            }
71            set
72            {
73                Position = new Vector(value, Position.Y);
74            }
75        }
76
77        /// <summary>
78        /// Kameran paikan Y-koordinaatti.
79        /// </summary>
80        public double Y
81        {
82            get
83            {
84                return Position.Y;
85            }
86            set
87            {
88                Position = new Vector(Position.X, value);
89            }
90        }
91
92        /// <summary>
93        /// Kameran zoomauskerroin.
94        /// Oletuksena 1.0. Mitä suurempi zoomauskerroin, sitä lähempänä kamera on (esim 2.0 on 2 x lähempänä) ja toisinpäin.
95        /// </summary>
96        public double ZoomFactor
97        {
98            get { return _zoomFactor; }
99            set { _zoomFactor = value; }
100        }
101
102        /// <summary>
103        /// Jos tosi, kamera ei mene koskaan kentän ulkopuolelle.
104        /// </summary>
105        public bool StayInLevel
106        {
107            get { return _stayInLevel; }
108            set { _stayInLevel = value; }
109        }
110
111        /// <summary>
112        /// Olio, jota kamera seuraa. Jos <c>null</c>, mitään oliota ei seurata.
113        /// </summary>
114        public GameObject FollowedObject { get; set; }
115
116        /// <summary>
117        /// Seurataanko oliota (FollowedObject) x- eli vaakasuunnassa.
118        /// </summary>
119        public bool FollowsX { get; set; }
120
121        /// <summary>
122        /// Seurataanko oliota (FollowedObject) y- eli pystysuunnassa.
123        /// </summary>
124        public bool FollowsY { get; set; }
125
126        /// <summary>
127        /// Jos kamera seuraa oliota, tällä voi säätää missä kohtaa ruutua pelaaja näkyy.
128        /// Toisin sanoen ruutukoordinaateissa kerrotaan, kuinka kaukana ruudun keskustasta
129        /// seurattava olio näkyy.
130        /// </summary>
131        /// <example>
132        /// Pelaajan näyttäminen ruudun alareunassa, vaakasuunnassa keskellä:
133        /// <code>
134        /// Camera.Follow( pelaaja );
135        /// Camera.FollowOffset = new Vector( 0, Screen.Height * 0.4 );
136        /// </code>
137        /// </example>
138        [Save] public Vector FollowOffset { get; set; }
139
140        /// <summary>
141        /// Jos kamera seuraa useita olioita, tällä voi säätää kuinka paljon vasempaan ja
142        /// oikeaan reunaan jätetään tyhjää tilaa.
143        /// </summary>
144        public double FollowXMargin { get; set; }
145
146        /// <summary>
147        /// Jos kamera seuraa useita olioita, tällä voi säätää kuinka paljon ylä- ja
148        /// alareunaan jätetään tyhjää tilaa.
149        /// </summary>
150        public double FollowYMargin { get; set; }
151
152        /// <summary>
153        /// Luo uuden kameran.
154        /// </summary>
155        internal Camera()
156        {
157            FollowsX = true;
158            FollowsY = true;
159            FollowXMargin = FollowYMargin = 600;
160        }
161
162        /// <summary>
163        /// Muuntaa annetun pisteen ruutukoordinaateista maailmankoordinaatteihin.
164        /// </summary>
165        public Vector ScreenToWorld(Vector point)
166        {
167            return Position + (1 / ZoomFactor) * point;
168        }
169
170        /// <summary>
171        /// Muuntaa annetun pisteen maailmankoordinaateista ruutukoordinaatteihin.
172        /// </summary>
173        public Vector WorldToScreen(Vector point)
174        {
175            return (point - Position) * ZoomFactor;
176        }
177
178        /// <summary>
179        /// Muuntaa annetun pisteen ruutukoordinaateista maailmankoordinaatteihin
180        /// ottaen huomioon oliokerroksen suhteellisen siirtymän.
181        /// </summary>
182        public Vector ScreenToWorld( Vector point, Layer layer )
183        {
184            if ( layer == null )
185                return ScreenToWorld( point );
186            if ( layer.IgnoresZoom )
187                return Vector.ComponentProduct( Position, layer.RelativeTransition ) + point;
188
189            return Vector.ComponentProduct( Position, layer.RelativeTransition ) + ( 1 / ZoomFactor ) * point;
190        }
191
192        /// <summary>
193        /// Kohdistaa kameran.
194        /// </summary>
195        /// <param name="v">Koordinaatti vektorina johon kamera kohdistetaan.</param>
196        public void Move( Vector v )
197        {
198            Position += new Vector( v.X / ZoomFactor, v.Y / ZoomFactor );
199        }
200
201        /// <summary>
202        /// Zoomaa.
203        /// </summary>
204        /// <param name="zoom">
205        /// Zoomauskerroin. Ykköstä suurempi (esim. 1.5) lähentää ja
206        /// ykköstä pienempi (esim. 0.5) zoomaa kauemmas.
207        /// </param>
208        public void Zoom( double zoom )
209        {
210            ZoomFactor *= zoom;
211        }
212
213        /// <summary>
214        /// Resetoi kameran (keskittää, laittaa zoomin oletusarvoon ja lopettaa seuraamisen).
215        /// </summary>
216        public void Reset()
217        {
218            Position = Vector.Zero;
219            Velocity = Vector.Zero;
220            ZoomFactor = 1.0f;
221            StopFollowing();
222        }
223
224        /// <summary>
225        /// Seuraa yhtä tai useampaa peliobjektia.
226        /// </summary>
227        /// <param name="gameobjects">Seurattavat peliobjektit.</param>
228        public void Follow( params GameObject[] gameobjects )
229        {
230            FollowsX = true;
231            FollowsY = true;
232
233            if ( gameobjects.Length == 0 ) return;
234            if ( gameobjects.Length == 1 )
235            {
236                FollowedObject = gameobjects[0];
237                return;
238            }
239
240            FollowedObject = new GameObject( 1.0, 1.0 );
241            followedObjects = new List<GameObject>( gameobjects );
242            updateAvgPoint();
243        }
244
245        /// <summary>
246        /// Seuraa jotakin peliobjektia X- eli vaakasuunnassa.
247        /// </summary>
248        /// <param name="gameobjects">Seurattavat peliobjektit.</param>
249        public void FollowX( params GameObject[] gameobjects )
250        {
251            Follow( gameobjects );
252            FollowsX = true;
253            FollowsY = false;
254        }
255
256        /// <summary>
257        /// Seuraa jotakin peliobjektia Y- eli pystysuunnassa.
258        /// </summary>
259        /// <param name="gameobjects">Seurattavat peliobjektit.</param>
260        public void FollowY( params GameObject[] gameobjects )
261        {
262            Follow( gameobjects );
263            FollowsX = false;
264            FollowsY = true;
265        }
266
267        /// <summary>
268        /// Lopettaa olio(iden) seuraamisen.
269        /// </summary>
270        public void StopFollowing()
271        {
272            if ( followedObjects != null )
273            {
274                followedObjects = null;
275                FollowedObject.Destroy();
276            }
277
278            FollowedObject = null;
279        }
280
281        private void updateAvgPoint()
282        {
283            FollowedObject.Position = followedObjects.ConvertAll<GameObject, Vector>( ( GameObject o ) => { return o.Position; } ).Average();
284
285            double maxDx = followedObjects.ConvertAll<GameObject, double>( ( GameObject o ) => { return Math.Abs( o.X - FollowedObject.X ); } ).Max();
286            double maxDy = followedObjects.ConvertAll<GameObject, double>( ( GameObject o ) => { return Math.Abs( o.Y - FollowedObject.Y ); } ).Max();
287
288            double zoomX = Game.Screen.Width / ( 2 * maxDx + FollowXMargin );
289            double zoomY = Game.Screen.Height / ( 2 * maxDy + FollowYMargin );
290
291            ZoomFactor = Math.Min( zoomX, zoomY );
292        }
293
294        /// <summary>
295        /// Zoomaa ja sijoittaa kameran niin, että parametreina annettua alue näkyy kokonaan ruudulla.
296        /// </summary>
297        /// <param name="bottomLeft">Alueen vasen alanurkka.</param>
298        /// <param name="topRight">Alueen oikea ylänurkka.</param>
299        public void ZoomTo( Vector bottomLeft, Vector topRight )
300        {
301            ZoomTo( bottomLeft.X, bottomLeft.Y, topRight.X, topRight.Y );
302        }
303
304        public void ZoomTo( BoundingRectangle rectangle )
305        {
306            ZoomTo( rectangle.Left, rectangle.Bottom, rectangle.Right, rectangle.Top );
307        }
308
309        public void ZoomTo( BoundingRectangle rectangle, double borderSize )
310        {
311            ZoomTo( rectangle.Left, rectangle.Bottom, rectangle.Right, rectangle.Top, borderSize );
312        }
313
314        public void ZoomToAllObjects()
315        {
316            ZoomToAllObjects( 0 );
317        }
318
319        /// <summary>
320        /// Zoomaa ja sijoittaa kameran siten, että kaikki pelioliot ovat yhtäaikaa näkyvissä.
321        /// </summary>
322        /// <param name="borderSize">Reunalle jätettävä tila (jos negatiivinen, niin osa kentästä jää piiloon).</param>
323        public void ZoomToAllObjects( double borderSize )
324        {
325            // Do the real zoom next update so all objects waiting to be added are added before that
326            Game.DoNextUpdate( doZoomToAllObjects, borderSize );
327        }
328
329        private void doZoomToAllObjects( double borderSize )
330        {
331            if ( Game.Instance.ObjectCount > 0 )
332                ZoomTo( Game.Instance.Level.FindObjectLimits(), borderSize );
333        }
334
335        /// <summary>
336        /// Zoomaa ja sijoittaa kameran niin, että parametreina annettua alue näkyy kokonaan ruudulla.
337        /// </summary>
338        /// <param name="left">Alueen vasemman reunan x-koordinaatti.</param>
339        /// <param name="bottom">Alueen alareunan y-koordinaatti.</param>
340        /// <param name="right">Alueen oikean reunan x-koordinaatti.</param>
341        /// <param name="top">Alueen yläreunan y-koordinaatti.</param>
342        public void ZoomTo( double left, double bottom, double right, double top )
343        {
344            ZoomTo( left, bottom, right, top, 0 );
345        }
346
347        internal void ZoomTo( double left, double bottom, double right, double top, double borderSize )
348        {
349            double screenWidth = (double)Game.Screen.Width;
350            double screenHeight = (double)Game.Screen.Height;
351            double width = right - left;
352            double height = top - bottom;
353
354            Position = new Vector( left + width / 2, bottom + height / 2 );
355
356            if ( ( width / height ) >= ( screenWidth / screenHeight ) )
357                this.ZoomFactor = screenWidth / ( width + borderSize );
358            else
359                this.ZoomFactor = screenHeight / ( height + borderSize );
360        }
361
362        /// <summary>
363        /// Zoomaa ja keskittää kameran siten, että koko kenttä on näkyvissä kerralla.
364        /// </summary>
365        public void ZoomToLevel()
366        {
367            ZoomToLevel( 0 );
368        }
369
370        /// <summary>
371        /// Zoomaa ja keskittää kameran siten, että koko kenttä on näkyvissä kerralla. Tällöin kamera ei seuraa mitään oliota.
372        /// </summary>
373        /// <param name="borderSize">Reunalle jätettävä tila (jos negatiivinen, niin osa kentästä jää piiloon).</param>
374        public void ZoomToLevel( double borderSize )
375        {
376            FollowedObject = null;
377            Level level = Game.Instance.Level;
378            ZoomTo( level.Left, level.Bottom, level.Right, level.Top, borderSize );
379        }
380
381        /// <summary>
382        /// Ajetaan kun pelitilannetta päivitetään.
383        /// </summary>
384        /// <param name="time">Peliaika</param>
385        internal void Update( Time time )
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
404            if (StayInLevel)
405            {
406                double screenWidth = (double)Game.Screen.Width;
407                double screenHeight = (double)Game.Screen.Height;
408                Level level = Game.Instance.Level;
409
410                double screenAspectRatio = screenWidth / screenHeight;
411                double levelAspectRatio = level.Width / level.Height;
412
413                //double zoomedWidth = level.Width * ZoomFactor;
414                //double zoomedHeight = level.Height * ZoomFactor;
415
416                double viewAreaWidth = screenWidth / ZoomFactor;
417                double viewAreaHeight = screenHeight / ZoomFactor;
418
419                /*
420                if ( zoomedWidth < screenWidth || zoomedHeight < screenHeight )
421                {
422                    ZoomFactor = Math.Max( screenWidth / level.Width, screenHeight / level.Height );
423                }*/
424
425
426
427                if (viewAreaWidth > level.Width)
428                {
429                    Position.X = level.Width / 2;
430                }
431                else
432                {
433                    if ((Position.X - (viewAreaWidth / 2)) < level.Left + level.Width / 2)
434                    {
435                        Position.X = level.Left + level.Width / 2 + (viewAreaWidth / 2);
436                    }
437                    else if (Position.X + (viewAreaWidth / 2) > level.Right + level.Width / 2)
438                    {
439                        Position.X = level.Right + level.Width / 2 - (viewAreaWidth / 2);
440                    }
441                }
442
443                if (viewAreaHeight > level.Height)
444                {
445                    Position.Y = level.Height / 2;
446                }
447                else
448                {
449                    if (Position.Y - (viewAreaHeight / 2) < level.Bottom + level.Height / 2)
450                    {
451                        Position.Y = level.Bottom + level.Height / 2 + (viewAreaHeight / 2);
452                    }
453                    else if (Position.Y + (viewAreaHeight / 2) > level.Top + level.Height / 2)
454                    {
455                        Position.Y = level.Top + level.Height / 2 - (viewAreaHeight / 2);
456                    }
457                }
458            }
459                Position.X = Math.Round(Position.X);
460                Position.Y = Math.Round(Position.Y);
461           
462        }
463    }
464}
Note: See TracBrowser for help on using the repository browser.