|
|
/*
|
|
|
OneLoneCoder - Camera2D v1.00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
A 2D world camera with various modes
|
|
|
|
|
|
|
|
|
License (OLC-3)
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
|
|
Copyright 2018 - 2022 OneLoneCoder.com
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
modification, are permitted provided that the following conditions
|
|
|
are met:
|
|
|
|
|
|
1. Redistributions or derivations of source code must retain the above
|
|
|
copyright notice, this list of conditions and the following disclaimer.
|
|
|
|
|
|
2. Redistributions or derivative works in binary form must reproduce
|
|
|
the above copyright notice. This list of conditions and the following
|
|
|
disclaimer must be reproduced in the documentation and/or other
|
|
|
materials provided with the distribution.
|
|
|
|
|
|
3. Neither the name of the copyright holder nor the names of its
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
Links
|
|
|
~~~~~
|
|
|
YouTube: https://www.youtube.com/javidx9
|
|
|
Discord: https://discord.gg/WhwHUMV
|
|
|
Twitter: https://www.twitter.com/javidx9
|
|
|
Twitch: https://www.twitch.tv/javidx9
|
|
|
GitHub: https://www.github.com/onelonecoder
|
|
|
Homepage: https://www.onelonecoder.com
|
|
|
|
|
|
Author
|
|
|
~~~~~~
|
|
|
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2019, 2020, 2021, 2022
|
|
|
|
|
|
*/
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
#include "olcPixelGameEngine.h"
|
|
|
|
|
|
namespace olc::utils
|
|
|
{
|
|
|
class Camera2D
|
|
|
{
|
|
|
public:
|
|
|
enum class Mode : uint8_t
|
|
|
{
|
|
|
Simple, // No motion, just directly settable
|
|
|
EdgeMove, // Moves as target crosses boundary
|
|
|
LazyFollow, // Lazily follows the target
|
|
|
FixedScreens, // Moves statically between "screens"
|
|
|
};
|
|
|
|
|
|
public:
|
|
|
inline Camera2D() : m_pTarget(&m_vLocalTarget) {}
|
|
|
|
|
|
// Construct a camera with a viewable area size, and an optional starting position
|
|
|
inline Camera2D(const olc::vf2d& vViewSize, const olc::vf2d& vViewPos = { 0.0f, 0.0f }) : m_pTarget(&m_vLocalTarget)
|
|
|
{
|
|
|
m_vViewSize = vViewSize;
|
|
|
m_vViewPos = vViewPos;
|
|
|
}
|
|
|
|
|
|
// Set the operational mode of this camera
|
|
|
inline void SetMode(const Mode t)
|
|
|
{
|
|
|
m_nMode = t;
|
|
|
}
|
|
|
|
|
|
// Get the operational mode of this camera
|
|
|
inline Mode GetMode() const
|
|
|
{
|
|
|
return m_nMode;
|
|
|
}
|
|
|
|
|
|
// Get the position of the target being tracked by this camera
|
|
|
inline const olc::vf2d& GetTarget() const
|
|
|
{
|
|
|
return *m_pTarget;
|
|
|
}
|
|
|
|
|
|
// Get the position of the cameras focus point
|
|
|
inline const olc::vf2d& GetPosition() const
|
|
|
{
|
|
|
return m_vPosition;
|
|
|
}
|
|
|
|
|
|
// Get the top left of teh cameras visible area in world space
|
|
|
inline const olc::vf2d& GetViewPosition() const
|
|
|
{
|
|
|
return m_vViewPos;
|
|
|
}
|
|
|
|
|
|
// Get the camera's visible area
|
|
|
inline const olc::vf2d& GetViewSize() const
|
|
|
{
|
|
|
return m_vViewSize;
|
|
|
}
|
|
|
|
|
|
// Set tracked point via pointer
|
|
|
inline void SetTarget(olc::vf2d& vTarget)
|
|
|
{
|
|
|
m_pTarget = &vTarget;
|
|
|
}
|
|
|
|
|
|
// Set tracked point via const ref - {10, 35} for example
|
|
|
inline void SetTarget(const olc::vf2d&& vTarget)
|
|
|
{
|
|
|
m_vLocalTarget = vTarget;
|
|
|
m_pTarget = &m_vLocalTarget;
|
|
|
}
|
|
|
|
|
|
// Set world boundary rectangle
|
|
|
inline void SetWorldBoundary(const olc::vf2d& vPos, const olc::vf2d& vSize)
|
|
|
{
|
|
|
m_vWorldBoundaryPos = vPos;
|
|
|
m_vWorldBoundarySize = vSize;
|
|
|
}
|
|
|
|
|
|
// Instruct camera to respect world boundaries
|
|
|
inline void EnableWorldBoundary(const bool bEnable)
|
|
|
{
|
|
|
m_bWorldBoundary = bEnable;
|
|
|
}
|
|
|
|
|
|
// Are we using a world boundary?
|
|
|
inline bool IsWorldBoundaryEnabled() const
|
|
|
{
|
|
|
return m_bWorldBoundary;
|
|
|
}
|
|
|
|
|
|
// Get the world boundary rectangle position
|
|
|
inline const olc::vf2d& GetWorldBoundaryPosition() const
|
|
|
{
|
|
|
return m_vWorldBoundaryPos;
|
|
|
}
|
|
|
|
|
|
// Get the world boundary rectangle size
|
|
|
inline const olc::vf2d& GetWorldBoundarySize() const
|
|
|
{
|
|
|
return m_vWorldBoundarySize;
|
|
|
}
|
|
|
|
|
|
// Set the velocity at which the lazy follower reaches tracked point
|
|
|
inline void SetLazyFollowRate(const float fRate)
|
|
|
{
|
|
|
m_fLazyFollowRate = fRate;
|
|
|
}
|
|
|
|
|
|
// Get the velocity at which the lazy follower reaches tracked point
|
|
|
inline float GetLazyFollowRate() const
|
|
|
{
|
|
|
return m_fLazyFollowRate;
|
|
|
}
|
|
|
|
|
|
// Set distance from tracked point to start nudging screen
|
|
|
inline void SetEdgeTriggerDistance(const olc::vf2d& vEdge)
|
|
|
{
|
|
|
m_vEdgeTriggerDistance = vEdge;
|
|
|
}
|
|
|
|
|
|
// Return disance from tracked point that screen will nudge
|
|
|
inline const olc::vf2d& GetEdgeTriggerDistance() const
|
|
|
{
|
|
|
return m_vEdgeTriggerDistance;
|
|
|
}
|
|
|
|
|
|
// Update camera, animating if necessary, obeying world boundary rules
|
|
|
// returns true if target is visible
|
|
|
inline virtual bool Update(const float fElapsedTime)
|
|
|
{
|
|
|
switch (m_nMode)
|
|
|
{
|
|
|
case Mode::Simple:
|
|
|
{
|
|
|
m_vPosition = GetTarget();
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case Mode::EdgeMove:
|
|
|
{
|
|
|
olc::vf2d vOverlap = GetTarget() - m_vPosition;
|
|
|
if (vOverlap.x > m_vEdgeTriggerDistance.x) m_vPosition.x += vOverlap.x - m_vEdgeTriggerDistance.x;
|
|
|
if (vOverlap.x < -m_vEdgeTriggerDistance.x) m_vPosition.x += vOverlap.x + m_vEdgeTriggerDistance.x;
|
|
|
if (vOverlap.y > m_vEdgeTriggerDistance.y) m_vPosition.y += vOverlap.y - m_vEdgeTriggerDistance.y;
|
|
|
if (vOverlap.y < -m_vEdgeTriggerDistance.y) m_vPosition.y += vOverlap.y + m_vEdgeTriggerDistance.y;
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case Mode::LazyFollow:
|
|
|
{
|
|
|
m_vPosition += (GetTarget() - m_vPosition) * m_fLazyFollowRate * fElapsedTime;
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case Mode::FixedScreens:
|
|
|
{
|
|
|
m_vPosition = olc::vf2d(olc::vi2d(GetTarget() / m_vScreenSize) * olc::vi2d(m_vScreenSize)) + (m_vViewSize * 0.5f);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// Make camera target the middle of the view
|
|
|
m_vViewPos = m_vPosition - (m_vViewSize * 0.5f);
|
|
|
|
|
|
// Clamp to World Boundary (if in place)
|
|
|
if (m_bWorldBoundary)
|
|
|
{
|
|
|
m_vViewPos = m_vViewPos.max(m_vWorldBoundaryPos).min(m_vWorldBoundaryPos + m_vWorldBoundarySize - m_vViewSize);
|
|
|
}
|
|
|
|
|
|
return GetTarget().x >= m_vViewPos.x && GetTarget().x < (m_vViewPos.x + m_vViewSize.x) &&
|
|
|
GetTarget().y >= m_vViewPos.y && GetTarget().y < (m_vViewPos.y + m_vViewSize.y);
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
// Position of camera focus point in the world
|
|
|
olc::vf2d m_vPosition;
|
|
|
// Rectangular size of camera viewing area
|
|
|
olc::vf2d m_vViewSize;
|
|
|
// Top left coordinate of camera viewing area
|
|
|
olc::vf2d m_vViewPos;
|
|
|
// Camera movement mode
|
|
|
Mode m_nMode = Mode::Simple;
|
|
|
|
|
|
// Target Vector2D object camera should follow (either ref or ptr)
|
|
|
olc::vf2d* m_pTarget = nullptr;
|
|
|
olc::vf2d m_vLocalTarget;
|
|
|
|
|
|
// World Boundary
|
|
|
bool m_bWorldBoundary = false;
|
|
|
olc::vf2d m_vWorldBoundaryPos = { 0.0f, 0.0f };
|
|
|
olc::vf2d m_vWorldBoundarySize = { 256.0f, 240.0f };
|
|
|
|
|
|
// Mode specific
|
|
|
olc::vf2d m_vEdgeTriggerDistance = { 1.0f, 1.0f };
|
|
|
float m_fLazyFollowRate = 4.0f;
|
|
|
olc::vi2d m_vScreenSize = { 16,15 };
|
|
|
};
|
|
|
} |