mirror of
https://github.com/borgesdan/xn65
synced 2024-12-29 21:54:47 +01:00
Implementações em PlatformStarterKit
This commit is contained in:
parent
912937a021
commit
a4fffeae62
@ -10,6 +10,7 @@ namespace xna {
|
|||||||
Game::Game() {
|
Game::Game() {
|
||||||
impl = unew<PlatformImplementation>();
|
impl = unew<PlatformImplementation>();
|
||||||
services = New<GameServiceContainer>();
|
services = New<GameServiceContainer>();
|
||||||
|
auto iservice = reinterpret_pointer_cast<IServiceProvider>(services);
|
||||||
_contentManager = New<ContentManager>("", services);
|
_contentManager = New<ContentManager>("", services);
|
||||||
|
|
||||||
_gameWindow = New<GameWindow>();
|
_gameWindow = New<GameWindow>();
|
||||||
|
@ -24,7 +24,7 @@ namespace xna {
|
|||||||
|
|
||||||
pad.IsConnected = state.connected;
|
pad.IsConnected = state.connected;
|
||||||
pad.PackedNumber = state.packet;
|
pad.PackedNumber = state.packet;
|
||||||
pad.Sticks = GamePadThumbSticks(
|
pad.ThumbSticks = GamePadThumbSticks(
|
||||||
Vector2(state.thumbSticks.leftX, state.thumbSticks.leftY),
|
Vector2(state.thumbSticks.leftX, state.thumbSticks.leftY),
|
||||||
Vector2(state.thumbSticks.rightX, state.thumbSticks.rightY));
|
Vector2(state.thumbSticks.rightX, state.thumbSticks.rightY));
|
||||||
pad.Triggers = GamePadTriggers(state.triggers.left, state.triggers.right);
|
pad.Triggers = GamePadTriggers(state.triggers.left, state.triggers.right);
|
||||||
@ -62,7 +62,7 @@ namespace xna {
|
|||||||
|
|
||||||
pad.IsConnected = state.connected;
|
pad.IsConnected = state.connected;
|
||||||
pad.PackedNumber = state.packet;
|
pad.PackedNumber = state.packet;
|
||||||
pad.Sticks = GamePadThumbSticks(
|
pad.ThumbSticks = GamePadThumbSticks(
|
||||||
Vector2(state.thumbSticks.leftX, state.thumbSticks.leftY),
|
Vector2(state.thumbSticks.leftX, state.thumbSticks.leftY),
|
||||||
Vector2(state.thumbSticks.rightX, state.thumbSticks.rightY));
|
Vector2(state.thumbSticks.rightX, state.thumbSticks.rightY));
|
||||||
pad.Triggers = GamePadTriggers(state.triggers.left, state.triggers.right);
|
pad.Triggers = GamePadTriggers(state.triggers.left, state.triggers.right);
|
||||||
|
@ -353,4 +353,26 @@ namespace xna {
|
|||||||
|
|
||||||
return texture2d;
|
return texture2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Int Texture2D::Width() const {
|
||||||
|
if (!impl) return 0;
|
||||||
|
|
||||||
|
return static_cast<Int>(impl->dxDescription.Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
Int Texture2D::Height() const {
|
||||||
|
if (!impl) return 0;
|
||||||
|
|
||||||
|
return static_cast<Int>(impl->dxDescription.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle Texture2D::Bounds() const {
|
||||||
|
if (!impl) return {};
|
||||||
|
|
||||||
|
return Rectangle(
|
||||||
|
0, 0,
|
||||||
|
static_cast<Int>(impl->dxDescription.Width),
|
||||||
|
static_cast<Int>(impl->dxDescription.Height)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@ -35,6 +35,8 @@ namespace xna {
|
|||||||
struct PlatformImplementation;
|
struct PlatformImplementation;
|
||||||
uptr<PlatformImplementation> impl = nullptr;
|
uptr<PlatformImplementation> impl = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PSoundEffect = sptr<SoundEffect>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -14,12 +14,12 @@ namespace xna {
|
|||||||
public:
|
public:
|
||||||
friend class ContentReader;
|
friend class ContentReader;
|
||||||
|
|
||||||
ContentManager(String const& rootDirectory, sptr<GameServiceContainer> const& services) :
|
ContentManager(String const& rootDirectory, sptr<IServiceProvider> const& services) :
|
||||||
_rootDirectory(rootDirectory){
|
_rootDirectory(rootDirectory){
|
||||||
_services = services;
|
_services = services;
|
||||||
};
|
};
|
||||||
|
|
||||||
static sptr<GameServiceContainer> Services() {
|
static sptr<IServiceProvider> Services() {
|
||||||
return _services;
|
return _services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ namespace xna {
|
|||||||
std::vector<Byte> byteBuffer;
|
std::vector<Byte> byteBuffer;
|
||||||
|
|
||||||
inline const static String contentExtension = ".xnb";
|
inline const static String contentExtension = ".xnb";
|
||||||
inline static sptr<GameServiceContainer> _services = nullptr;
|
inline static sptr<IServiceProvider> _services = nullptr;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +144,9 @@ namespace xna {
|
|||||||
|
|
||||||
//TODO: Not implemented.
|
//TODO: Not implemented.
|
||||||
//static constexpr TimeSpan FromMinutes(int64_t minutes, int64_t seconds = 0, int64_t milliseconds = 0, int64_t microseconds = 0);
|
//static constexpr TimeSpan FromMinutes(int64_t minutes, int64_t seconds = 0, int64_t milliseconds = 0, int64_t microseconds = 0);
|
||||||
|
static constexpr TimeSpan FromSeconds(int seconds) {
|
||||||
|
return FromSeconds(static_cast<int64_t>(seconds));
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr TimeSpan FromSeconds(int64_t seconds) {
|
static constexpr TimeSpan FromSeconds(int64_t seconds) {
|
||||||
return FromUnits(seconds, TicksPerSecond, MinSeconds, MaxSeconds);
|
return FromUnits(seconds, TicksPerSecond, MinSeconds, MaxSeconds);
|
||||||
@ -292,11 +295,11 @@ namespace xna {
|
|||||||
return a.Divide(b);
|
return a.Divide(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend TimeSpan operator<(TimeSpan const& a, TimeSpan const& b) {
|
friend bool operator<(TimeSpan const& a, TimeSpan const& b) {
|
||||||
return a._ticks < b._ticks;
|
return a._ticks < b._ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend TimeSpan operator<=(TimeSpan const& a, TimeSpan const& b) {
|
friend bool operator<=(TimeSpan const& a, TimeSpan const& b) {
|
||||||
return a._ticks <= b._ticks;
|
return a._ticks <= b._ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,18 +20,58 @@ namespace xna {
|
|||||||
Matrix const& transformMatrix = Matrix::Identity()
|
Matrix const& transformMatrix = Matrix::Identity()
|
||||||
);
|
);
|
||||||
void End();
|
void End();
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Vector2 const& position, Color const& color) {
|
||||||
|
Draw(*texture, position, color);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Vector2 const& position, Color const& color);
|
void Draw(Texture2D& texture, Vector2 const& position, Color const& color);
|
||||||
|
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color) {
|
||||||
|
Draw(*texture, position, sourceRectangle, color);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color);
|
void Draw(Texture2D& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color);
|
||||||
|
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color,
|
||||||
|
float rotation, Vector2 const& origin, float scale, SpriteEffects effects, float layerDepth) {
|
||||||
|
Draw(*texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color,
|
void Draw(Texture2D& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color,
|
||||||
float rotation, Vector2 const& origin, float scale, SpriteEffects effects, float layerDepth);
|
float rotation, Vector2 const& origin, float scale, SpriteEffects effects, float layerDepth);
|
||||||
|
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color,
|
||||||
|
float rotation, Vector2 const& origin, Vector2 const& scale, SpriteEffects effects, float layerDepth) {
|
||||||
|
Draw(*texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color,
|
void Draw(Texture2D& texture, Vector2 const& position, Rectangle const* sourceRectangle, Color const& color,
|
||||||
float rotation, Vector2 const& origin, Vector2 const& scale, SpriteEffects effects, float layerDepth);
|
float rotation, Vector2 const& origin, Vector2 const& scale, SpriteEffects effects, float layerDepth);
|
||||||
|
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Rectangle const& destinationRectangle, Color const& color) {
|
||||||
|
Draw(*texture, destinationRectangle, color);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Rectangle const& destinationRectangle, Color const& color);
|
void Draw(Texture2D& texture, Rectangle const& destinationRectangle, Color const& color);
|
||||||
|
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Rectangle const& destinationRectangle, Rectangle const* sourceRectangle, Color const& color) {
|
||||||
|
Draw(*texture, destinationRectangle, sourceRectangle, color);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Rectangle const& destinationRectangle, Rectangle const* sourceRectangle, Color const& color);
|
void Draw(Texture2D& texture, Rectangle const& destinationRectangle, Rectangle const* sourceRectangle, Color const& color);
|
||||||
|
|
||||||
|
void Draw(sptr<Texture2D> const& texture, Rectangle const& destinationRectangle, Rectangle const* sourceRectangle, Color const& color,
|
||||||
|
float rotation, Vector2 const& origin, SpriteEffects effects, float layerDepth) {
|
||||||
|
Draw(*texture, destinationRectangle, sourceRectangle, color, rotation, origin, effects, layerDepth);
|
||||||
|
}
|
||||||
void Draw(Texture2D& texture, Rectangle const& destinationRectangle, Rectangle const* sourceRectangle, Color const& color,
|
void Draw(Texture2D& texture, Rectangle const& destinationRectangle, Rectangle const* sourceRectangle, Color const& color,
|
||||||
float rotation, Vector2 const& origin, SpriteEffects effects, float layerDepth);
|
float rotation, Vector2 const& origin, SpriteEffects effects, float layerDepth);
|
||||||
|
|
||||||
void Viewport(xna::Viewport const& value);
|
void Viewport(xna::Viewport const& value);
|
||||||
|
|
||||||
|
void DrawString(sptr<SpriteFont> const& spriteFont, String const& text, Vector2 const& position, Color const& color) {
|
||||||
|
DrawString(*spriteFont, text, position, color);
|
||||||
|
}
|
||||||
void DrawString(SpriteFont& spriteFont, String const& text, Vector2 const& position, Color const& color);
|
void DrawString(SpriteFont& spriteFont, String const& text, Vector2 const& position, Color const& color);
|
||||||
|
|
||||||
|
void DrawString(sptr<SpriteFont> const& spriteFont, String const& text, Vector2 const& position, Color const& color,
|
||||||
|
float rotation, Vector2 const& origin, float scale, SpriteEffects effects, float layerDepth) {
|
||||||
|
DrawString(*spriteFont, text, position, color, rotation, origin, scale, effects, layerDepth);
|
||||||
|
}
|
||||||
void DrawString(SpriteFont& spriteFont, String const& text, Vector2 const& position, Color const& color,
|
void DrawString(SpriteFont& spriteFont, String const& text, Vector2 const& position, Color const& color,
|
||||||
float rotation, Vector2 const& origin, float scale, SpriteEffects effects, float layerDepth);
|
float rotation, Vector2 const& origin, float scale, SpriteEffects effects, float layerDepth);
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ namespace xna {
|
|||||||
|
|
||||||
|
|
||||||
using PTexture2D = sptr<Texture2D>;
|
using PTexture2D = sptr<Texture2D>;
|
||||||
using UTexture2D = uptr<Texture2D>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -193,21 +193,21 @@ namespace xna {
|
|||||||
case xna::Buttons::BigButton:
|
case xna::Buttons::BigButton:
|
||||||
return this->Buttons.BigButton == ButtonState::Pressed;
|
return this->Buttons.BigButton == ButtonState::Pressed;
|
||||||
case xna::Buttons::LeftThumbstickLeft:
|
case xna::Buttons::LeftThumbstickLeft:
|
||||||
return this->Sticks.Left().X < 0.5F;
|
return this->ThumbSticks.Left().X < 0.5F;
|
||||||
case xna::Buttons::LeftThumbstickRight:
|
case xna::Buttons::LeftThumbstickRight:
|
||||||
return this->Sticks.Left().X > 0.5F;
|
return this->ThumbSticks.Left().X > 0.5F;
|
||||||
case xna::Buttons::LeftThumbstickDown:
|
case xna::Buttons::LeftThumbstickDown:
|
||||||
return this->Sticks.Left().Y > 0.5F;
|
return this->ThumbSticks.Left().Y > 0.5F;
|
||||||
case xna::Buttons::LeftThumbstickUp:
|
case xna::Buttons::LeftThumbstickUp:
|
||||||
return this->Sticks.Left().Y < 0.5F;
|
return this->ThumbSticks.Left().Y < 0.5F;
|
||||||
case xna::Buttons::RightThumbstickLeft:
|
case xna::Buttons::RightThumbstickLeft:
|
||||||
return this->Sticks.Right().X < 0.5F;
|
return this->ThumbSticks.Right().X < 0.5F;
|
||||||
case xna::Buttons::RightThumbstickRight:
|
case xna::Buttons::RightThumbstickRight:
|
||||||
return this->Sticks.Right().X > 0.5F;
|
return this->ThumbSticks.Right().X > 0.5F;
|
||||||
case xna::Buttons::RightThumbstickDown:
|
case xna::Buttons::RightThumbstickDown:
|
||||||
return this->Sticks.Right().Y > 0.5F;
|
return this->ThumbSticks.Right().Y > 0.5F;
|
||||||
case xna::Buttons::RightThumbstickUp:
|
case xna::Buttons::RightThumbstickUp:
|
||||||
return this->Sticks.Right().Y < 0.5F;
|
return this->ThumbSticks.Right().Y < 0.5F;
|
||||||
case xna::Buttons::LeftTrigger:
|
case xna::Buttons::LeftTrigger:
|
||||||
return this->Triggers.Left() > 0.5F;
|
return this->Triggers.Left() > 0.5F;
|
||||||
case xna::Buttons::RightTrigger:
|
case xna::Buttons::RightTrigger:
|
||||||
@ -225,7 +225,7 @@ namespace xna {
|
|||||||
GamePadDPad Dpad{};
|
GamePadDPad Dpad{};
|
||||||
bool IsConnected{false};
|
bool IsConnected{false};
|
||||||
Ulong PackedNumber{0};
|
Ulong PackedNumber{0};
|
||||||
GamePadThumbSticks Sticks{};
|
GamePadThumbSticks ThumbSticks{};
|
||||||
GamePadTriggers Triggers{};
|
GamePadTriggers Triggers{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#define NOMINMAX
|
||||||
#include "xnaerror.hpp"
|
#include "xnaerror.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
13
samples/02_PlatfformerStarterKit/CMakeLists.txt
Normal file
13
samples/02_PlatfformerStarterKit/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# CMakeList.txt : CMake project for CmakeSharedExeTest, include source and define
|
||||||
|
# project specific logic here.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Add source to this project's executable.
|
||||||
|
add_executable (BlankApp WIN32 "game.cpp" "animation.cpp" "enemy.cpp" "level.cpp" "player.cpp" "gem.cpp")
|
||||||
|
|
||||||
|
if (CMAKE_VERSION VERSION_GREATER 3.12)
|
||||||
|
set_property(TARGET BlankApp PROPERTY CXX_STANDARD 20)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: Add tests and install targets if needed.
|
||||||
|
target_link_libraries(BlankApp Xn65)
|
39
samples/02_PlatfformerStarterKit/animation.cpp
Normal file
39
samples/02_PlatfformerStarterKit/animation.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
void AnimationPlayer::PlayAnimation(xna::sptr<PlatformerStarterKit::Animation> const& animation) {
|
||||||
|
if (this->animation == animation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Start the new animation.
|
||||||
|
this->animation = animation;
|
||||||
|
this->frameIndex = 0;
|
||||||
|
this->time = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch, xna::Vector2& position, xna::SpriteEffects spriteEffects) {
|
||||||
|
if (animation == nullptr)
|
||||||
|
std::exception();
|
||||||
|
|
||||||
|
// Process passing time.
|
||||||
|
time += (float)gameTime.ElapsedGameTime.TotalSeconds();
|
||||||
|
|
||||||
|
while (time > animation->FrameTime()) {
|
||||||
|
time -= animation->FrameTime();
|
||||||
|
|
||||||
|
// Advance the frame index; looping or clamping as appropriate.
|
||||||
|
if (animation->IsLooping()) {
|
||||||
|
frameIndex = (frameIndex + 1) % animation->FrameCount();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frameIndex = std::min(frameIndex + 1, animation->FrameCount() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the source rectangle of the current frame.
|
||||||
|
const auto source = xna::Rectangle(frameIndex * animation->Texture()->Height(), 0, animation->Texture()->Height(), animation->Texture()->Height());
|
||||||
|
|
||||||
|
// Draw the current frame.
|
||||||
|
spriteBatch.Draw(animation->Texture(), position, &source, xna::Colors::White, 0.0f, Origin(), 1.0f, spriteEffects, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
75
samples/02_PlatfformerStarterKit/animation.hpp
Normal file
75
samples/02_PlatfformerStarterKit/animation.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_ANIMATION_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_ANIMATION_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
/*
|
||||||
|
* Represents an animated texture.
|
||||||
|
* Currently, this class assumes that each frame of animation is
|
||||||
|
* as wide as each animation is tall. The number of frames in the
|
||||||
|
* animation are inferred from this.
|
||||||
|
*/
|
||||||
|
class Animation {
|
||||||
|
public:
|
||||||
|
Animation(xna::sptr<xna::Texture2D> const& texture, float frameTime, bool isLooping):
|
||||||
|
texture(texture), frameTime(frameTime), isLooping(isLooping){}
|
||||||
|
|
||||||
|
inline xna::sptr<xna::Texture2D> Texture() const {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float FrameTime() const {
|
||||||
|
return frameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsLooping() const {
|
||||||
|
return isLooping;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int FrameCount() const {
|
||||||
|
return texture->Width() / texture->Height();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int FrameWidth() const {
|
||||||
|
return texture->Height();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int FrameHeight() const {
|
||||||
|
return texture->Height();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
xna::sptr<xna::Texture2D> texture = nullptr;
|
||||||
|
float frameTime = 0;
|
||||||
|
bool isLooping = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Controls playback of an Animation.
|
||||||
|
struct AnimationPlayer {
|
||||||
|
xna::sptr<Animation> Animation() const {
|
||||||
|
return animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int FrameIndex() const {
|
||||||
|
return frameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline xna::Vector2 Origin() const {
|
||||||
|
return xna::Vector2(
|
||||||
|
animation->FrameWidth() / 2.0f,
|
||||||
|
animation->FrameHeight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayAnimation(xna::sptr<PlatformerStarterKit::Animation> const& animation);
|
||||||
|
void Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch, xna::Vector2& position, xna::SpriteEffects spriteEffects);
|
||||||
|
|
||||||
|
private:
|
||||||
|
xna::sptr<PlatformerStarterKit::Animation> animation = nullptr;
|
||||||
|
int frameIndex = 0;
|
||||||
|
float time = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
28
samples/02_PlatfformerStarterKit/circle.hpp
Normal file
28
samples/02_PlatfformerStarterKit/circle.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_CIRCLE_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_CIRCLE_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
//Represents a 2D circle.
|
||||||
|
struct Circle {
|
||||||
|
xna::Vector2 Center{};
|
||||||
|
float Radius{ 0 };
|
||||||
|
|
||||||
|
constexpr Circle() = default;
|
||||||
|
constexpr Circle(xna::Vector2 const& position, float radius):
|
||||||
|
Center(position), Radius(radius){}
|
||||||
|
|
||||||
|
constexpr bool Intersects(xna::Rectangle const& rectangle) const {
|
||||||
|
const auto v = xna::Vector2(xna::MathHelper::Clamp(Center.X, rectangle.Left(), rectangle.Right()),
|
||||||
|
xna::MathHelper::Clamp(Center.Y, rectangle.Top(), rectangle.Bottom()));
|
||||||
|
|
||||||
|
const auto direction = Center - v;
|
||||||
|
auto distanceSquared = direction.LengthSquared();
|
||||||
|
|
||||||
|
return ((distanceSquared > 0) && (distanceSquared < Radius * Radius));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
79
samples/02_PlatfformerStarterKit/enemy.cpp
Normal file
79
samples/02_PlatfformerStarterKit/enemy.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "enemy.hpp"
|
||||||
|
#include "level.hpp"
|
||||||
|
#include "player.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
Enemy::Enemy(xna::sptr<PlatformerStarterKit::Level> const& level, xna::Vector2 const& position, xna::String const& spriteSet):
|
||||||
|
level(level), position(position)
|
||||||
|
{
|
||||||
|
LoadContent(spriteSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> Enemy::Level() const
|
||||||
|
{
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::Rectangle Enemy::BoundingRectangle() const
|
||||||
|
{
|
||||||
|
int left = static_cast<int>(std::round(position.X - sprite.Origin().X)) + localBounds.X;
|
||||||
|
int top = static_cast<int>(std::round(position.Y - sprite.Origin().Y)) + localBounds.Y;
|
||||||
|
|
||||||
|
return xna::Rectangle(left, top, localBounds.Width, localBounds.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::LoadContent(xna::String const& spriteSet)
|
||||||
|
{
|
||||||
|
auto _spriteSet = "Sprites/" + spriteSet + "/";
|
||||||
|
runAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>(_spriteSet + "Run"), 0.1f, true);
|
||||||
|
idleAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>(_spriteSet + "Idle"), 0.15f, true);
|
||||||
|
sprite.PlayAnimation(idleAnimation);
|
||||||
|
|
||||||
|
const auto width = static_cast<int>(idleAnimation->FrameWidth() * 0.35);
|
||||||
|
const auto left = (idleAnimation->FrameWidth() - width) / 2;
|
||||||
|
const auto height = static_cast<int>(idleAnimation->FrameWidth() * 0.7);
|
||||||
|
const auto top = idleAnimation->FrameHeight() - height;
|
||||||
|
localBounds = xna::Rectangle(left, top, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::Update(xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
const auto elapsed = static_cast<float>(gameTime.ElapsedGameTime.TotalSeconds());
|
||||||
|
|
||||||
|
const auto posX = position.X + localBounds.Width / 2 * (int)direction;
|
||||||
|
const auto tileX = static_cast<int>(std::floor(posX / Tile::Width)) - static_cast<int>(direction);
|
||||||
|
const auto tileY = static_cast<int>(std::floor(position.Y / Tile::Height));
|
||||||
|
|
||||||
|
if (waitTime > 0) {
|
||||||
|
waitTime = std::max(0.0f, waitTime - static_cast<float>(gameTime.ElapsedGameTime.TotalSeconds()));
|
||||||
|
if (waitTime <= 0.0f) {
|
||||||
|
direction = (FaceDirection)(-static_cast<int>(direction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (level->GetCollision(tileX + static_cast<int>(direction), tileY - 1) == TileCollision::Impassable ||
|
||||||
|
level->GetCollision(tileX + static_cast<int>(direction), tileY) == TileCollision::Passable) {
|
||||||
|
waitTime = MaxWaitTime;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const auto velocity = xna::Vector2(static_cast<int>(direction) * MoveSpeed * elapsed, 0.0f);
|
||||||
|
position = position + velocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Enemy::Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch)
|
||||||
|
{
|
||||||
|
if (!level->Player()->IsAlive() ||
|
||||||
|
level->ReachedExit() ||
|
||||||
|
level->TimeRemaining() == xna::TimeSpan::Zero() ||
|
||||||
|
waitTime > 0) {
|
||||||
|
sprite.PlayAnimation(idleAnimation);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprite.PlayAnimation(runAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto flip = static_cast<int>(direction) > 0 ? xna::SpriteEffects::FlipHorizontally : xna::SpriteEffects::None;
|
||||||
|
sprite.Draw(gameTime, spriteBatch, position, flip);
|
||||||
|
}
|
||||||
|
}
|
51
samples/02_PlatfformerStarterKit/enemy.hpp
Normal file
51
samples/02_PlatfformerStarterKit/enemy.hpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_ENEMY_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_ENEMY_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
//Facing direction along the X axis.
|
||||||
|
enum class FaceDirection {
|
||||||
|
Left = -1,
|
||||||
|
Right = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Level;
|
||||||
|
|
||||||
|
// A monster who is impeding the progress of our fearless adventurer.
|
||||||
|
class Enemy {
|
||||||
|
public:
|
||||||
|
Enemy(xna::sptr<PlatformerStarterKit::Level> const& level, xna::Vector2 const& position, xna::String const& spriteSet);
|
||||||
|
|
||||||
|
public:
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> Level() const;
|
||||||
|
|
||||||
|
constexpr xna::Vector2 Position() const {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::Rectangle BoundingRectangle() const;
|
||||||
|
|
||||||
|
void LoadContent(xna::String const& spriteSet);
|
||||||
|
void Update(xna::GameTime const& gameTime);
|
||||||
|
void Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr float MaxWaitTime = 0.5f;
|
||||||
|
static constexpr float MoveSpeed = 128.0f;
|
||||||
|
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> level;
|
||||||
|
xna::Vector2 position{};
|
||||||
|
xna::Rectangle localBounds{};
|
||||||
|
|
||||||
|
xna::sptr<Animation> runAnimation = nullptr;
|
||||||
|
xna::sptr<Animation> idleAnimation = nullptr;
|
||||||
|
AnimationPlayer sprite{};
|
||||||
|
FaceDirection direction = FaceDirection::Left;
|
||||||
|
|
||||||
|
float waitTime = 0.0F;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
36
samples/02_PlatfformerStarterKit/extensions.hpp
Normal file
36
samples/02_PlatfformerStarterKit/extensions.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_EXTENSIONS_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_EXTENSIONS_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
struct RectangleExtensions {
|
||||||
|
static xna::Vector2 GetIntersectionDepth(xna::Rectangle const& rectA, xna::Rectangle const& rectB) {
|
||||||
|
const auto halfWidthA = rectA.Width / 2.0f;
|
||||||
|
const auto halfHeightA = rectA.Height / 2.0f;
|
||||||
|
const auto halfWidthB = rectB.Width / 2.0f;
|
||||||
|
const auto halfHeightB = rectB.Height / 2.0f;
|
||||||
|
|
||||||
|
const auto centerA = xna::Vector2(rectA.Left() + halfWidthA, rectA.Top() + halfHeightA);
|
||||||
|
const auto centerB = xna::Vector2(rectB.Left() + halfWidthB, rectB.Top() + halfHeightB);
|
||||||
|
|
||||||
|
const auto distanceX = centerA.X - centerB.X;
|
||||||
|
const auto distanceY = centerA.Y - centerB.Y;
|
||||||
|
const auto minDistanceX = halfWidthA + halfWidthB;
|
||||||
|
const auto minDistanceY = halfHeightA + halfHeightB;
|
||||||
|
|
||||||
|
if (std::abs(distanceX) >= minDistanceX || std::abs(distanceY) >= minDistanceY)
|
||||||
|
return xna::Vector2::Zero();
|
||||||
|
|
||||||
|
const auto depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
|
||||||
|
const auto depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
|
||||||
|
return xna::Vector2(depthX, depthY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr xna::Vector2 GetBottomCenter(xna::Rectangle const& rect) {
|
||||||
|
return xna::Vector2(rect.X + rect.Width / 2.0f, rect.Bottom());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
74
samples/02_PlatfformerStarterKit/game.cpp
Normal file
74
samples/02_PlatfformerStarterKit/game.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// xna.cpp : Defines the entry point for the application.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
#include "player.hpp"
|
||||||
|
#include "enemy.hpp"
|
||||||
|
#include "level.hpp"
|
||||||
|
#include "gem.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace xna;
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
class Game1 : public Game {
|
||||||
|
public:
|
||||||
|
Game1() : Game() {
|
||||||
|
Content()->RootDirectory("Content");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize() override {
|
||||||
|
auto game = reinterpret_cast<Game*>(this);
|
||||||
|
graphics = New<GraphicsDeviceManager>(game->shared_from_this());
|
||||||
|
graphics->Initialize();
|
||||||
|
|
||||||
|
std::any device = graphicsDevice;
|
||||||
|
services->AddService(*typeof<GraphicsDevice>(), device);
|
||||||
|
|
||||||
|
Game::Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadContent() override {
|
||||||
|
spriteBatch = New<SpriteBatch>(*graphicsDevice);
|
||||||
|
|
||||||
|
Game::LoadContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(GameTime const& gameTime) override {
|
||||||
|
if (Keyboard::GetState().IsKeyDown(Keys::Escape) || GamePad::GetState(PlayerIndex::One).IsButtonDown(Buttons::Back))
|
||||||
|
Exit();
|
||||||
|
|
||||||
|
Game::Update(gameTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(GameTime const& gameTime) override {
|
||||||
|
graphicsDevice->Clear(Colors::CornflowerBlue);
|
||||||
|
Game::Draw(gameTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
sptr<GraphicsDeviceManager> graphics = nullptr;
|
||||||
|
sptr<SpriteBatch> spriteBatch = nullptr;
|
||||||
|
sptr<SpriteFont> hudFont = nullptr;
|
||||||
|
sptr<Texture2D> winOverlay = nullptr;
|
||||||
|
sptr<Texture2D> loseOverlay = nullptr;
|
||||||
|
sptr<Texture2D> diedOverlay = nullptr;
|
||||||
|
int levelIndex = -1;
|
||||||
|
sptr<Level> level = nullptr;
|
||||||
|
bool wasContinuePressed;
|
||||||
|
TimeSpan WarningTime = TimeSpan::FromSeconds(30);
|
||||||
|
static constexpr int TargetFrameRate = 60;
|
||||||
|
static constexpr int BackBufferWidth = 1280;
|
||||||
|
static constexpr int BackBufferHeight = 720;
|
||||||
|
static constexpr Buttons ContinueButton = Buttons::A;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
|
||||||
|
xna::Platform::Init();
|
||||||
|
|
||||||
|
auto game = snew<PlatformerStarterKit::Game1>();
|
||||||
|
const auto result = game->Run();
|
||||||
|
return result;
|
||||||
|
}
|
38
samples/02_PlatfformerStarterKit/gem.cpp
Normal file
38
samples/02_PlatfformerStarterKit/gem.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "gem.hpp"
|
||||||
|
#include "level.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
Gem::Gem(xna::sptr<PlatformerStarterKit::Level> const& level, xna::Vector2 const& position) :
|
||||||
|
level(level), basePosition(position){
|
||||||
|
LoadContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gem::LoadContent()
|
||||||
|
{
|
||||||
|
texture = level->Content()->Load<xna::PTexture2D>("Sprites/Gem");
|
||||||
|
origin = xna::Vector2(texture->Width() / 2.0f, texture->Height() / 2.0f);
|
||||||
|
collectedSound = level->Content()->Load<xna::PSoundEffect>("Sounds/GemCollected");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gem::Update(xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
constexpr float BounceHeight = 0.18f;
|
||||||
|
constexpr float BounceRate = 3.0f;
|
||||||
|
constexpr float BounceSync = -0.75f;
|
||||||
|
|
||||||
|
const auto t = gameTime.TotalGameTime.TotalSeconds()
|
||||||
|
* BounceRate + Position().X * BounceSync;
|
||||||
|
|
||||||
|
bounce = static_cast<float>(std::sin(t)) * BounceHeight * texture->Height();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gem::OnCollected(xna::sptr<Player>& collectedBy)
|
||||||
|
{
|
||||||
|
collectedSound->Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gem::Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch)
|
||||||
|
{
|
||||||
|
spriteBatch.Draw(texture, Position(), nullptr, Color, 0.0f, origin, 1.0f, xna::SpriteEffects::None, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
45
samples/02_PlatfformerStarterKit/gem.hpp
Normal file
45
samples/02_PlatfformerStarterKit/gem.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_GEM_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_GEM_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
#include "circle.hpp"
|
||||||
|
#include "tile.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
class Level;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class Gem {
|
||||||
|
public:
|
||||||
|
Gem(xna::sptr<PlatformerStarterKit::Level> const& level, xna::Vector2 const& position);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr int PointValue = 30;
|
||||||
|
static constexpr xna::Color Color = xna::Colors::White;
|
||||||
|
|
||||||
|
constexpr xna::Vector2 Position() const {
|
||||||
|
return basePosition + xna::Vector2(0.0f, bounce);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Circle BoundingCircle() const {
|
||||||
|
return Circle(Position(), Tile::Width / 3.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> Level() const;
|
||||||
|
|
||||||
|
void LoadContent();
|
||||||
|
void Update(xna::GameTime const& gameTime);
|
||||||
|
void OnCollected(xna::sptr<Player>& collectedBy);
|
||||||
|
void Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
xna::PTexture2D texture = nullptr;
|
||||||
|
xna::Vector2 origin{};
|
||||||
|
xna::PSoundEffect collectedSound = nullptr;
|
||||||
|
xna::Vector2 basePosition{};
|
||||||
|
float bounce{ 0.0F };
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> level = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
257
samples/02_PlatfformerStarterKit/level.cpp
Normal file
257
samples/02_PlatfformerStarterKit/level.cpp
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#include "enemy.hpp"
|
||||||
|
#include "extensions.hpp"
|
||||||
|
#include "level.hpp"
|
||||||
|
#include "player.hpp"
|
||||||
|
#include "gem.hpp"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
Level::Level(xna::sptr<xna::IServiceProvider> const& serviceProvider, xna::String const& path)
|
||||||
|
{
|
||||||
|
srand(354668);
|
||||||
|
|
||||||
|
content = xna::snew<xna::ContentManager>("Content", serviceProvider);
|
||||||
|
timeRemaining = xna::TimeSpan::FromMinutes(2.0);
|
||||||
|
|
||||||
|
LoadTiles(path);
|
||||||
|
|
||||||
|
layers = std::vector<xna::PTexture2D>(3);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < layers.size(); ++i) {
|
||||||
|
const auto segmentIndex = rand() % 3;
|
||||||
|
layers[i] = content->Load<xna::PTexture2D>("Backgrounds/Layer" + i + '_' + segmentIndex);
|
||||||
|
|
||||||
|
exitReachedSound = content->Load<xna::PSoundEffect>("Sounds/ExitReached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xna::sptr<PlatformerStarterKit::Player> Level::Player() const
|
||||||
|
{
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::LoadTiles(xna::String const& path)
|
||||||
|
{
|
||||||
|
int width = 0;
|
||||||
|
std::vector<xna::String> lines;
|
||||||
|
|
||||||
|
std::fstream reader(path);
|
||||||
|
xna::String line;
|
||||||
|
std::getline(reader, line);
|
||||||
|
width = line.size();
|
||||||
|
|
||||||
|
while (!reader.eofbit) {
|
||||||
|
if (line.size() != width)
|
||||||
|
std::exception("The length of line {0} is different from all preceeding lines.");
|
||||||
|
|
||||||
|
lines.push_back(line);
|
||||||
|
line.clear();
|
||||||
|
std::getline(reader, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles = std::vector<std::vector<Tile>>(width, std::vector<Tile>(lines.size()));
|
||||||
|
|
||||||
|
for (size_t y = 0; y < lines.size(); ++y) {
|
||||||
|
for (size_t x = 0; x < width; ++x) {
|
||||||
|
auto tileType = lines[y][x];
|
||||||
|
tiles[x][y] = LoadTile(tileType, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
std::exception("A level must have a starting point.");
|
||||||
|
if (exit == InvalidPosition)
|
||||||
|
std::exception("A level must have an exit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadTile(char tileType, int x, int y) {
|
||||||
|
switch (tileType) {
|
||||||
|
case '.':
|
||||||
|
return Tile(nullptr, TileCollision::Passable);
|
||||||
|
case 'X':
|
||||||
|
return LoadExitTile(x, y);
|
||||||
|
case 'G':
|
||||||
|
return LoadGemTile(x, y);
|
||||||
|
case '-':
|
||||||
|
return LoadTile("Platform", TileCollision::Platform);
|
||||||
|
case 'A':
|
||||||
|
return LoadEnemyTile(x, y, "MonsterA");
|
||||||
|
case 'B':
|
||||||
|
return LoadEnemyTile(x, y, "MonsterB");
|
||||||
|
case 'C':
|
||||||
|
return LoadEnemyTile(x, y, "MonsterC");
|
||||||
|
case 'D':
|
||||||
|
return LoadEnemyTile(x, y, "MonsterD");
|
||||||
|
case '~':
|
||||||
|
return LoadVarietyTile("BlockB", 2, TileCollision::Platform);
|
||||||
|
case ':':
|
||||||
|
return LoadVarietyTile("BlockB", 2, TileCollision::Passable);
|
||||||
|
case '1':
|
||||||
|
return LoadStartTile(x, y);
|
||||||
|
case '#':
|
||||||
|
return LoadVarietyTile("BlockA", 7, TileCollision::Impassable);
|
||||||
|
default:
|
||||||
|
std::exception("Unsupported tile type character");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadTile(xna::String const& name, TileCollision collision) {
|
||||||
|
return Tile(content->Load<xna::PTexture2D>("Tiles/" + name), collision);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadVarietyTile(xna::String const& baseName, int variationCount, TileCollision collision) {
|
||||||
|
auto index = rand() % variationCount;
|
||||||
|
return LoadTile(baseName + std::to_string(index), collision);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadStartTile(int x, int y) {
|
||||||
|
if (player != nullptr)
|
||||||
|
std::exception("A level may only have one starting point.");
|
||||||
|
|
||||||
|
start = RectangleExtensions::GetBottomCenter(GetBounds(x, y));
|
||||||
|
const auto _this = shared_from_this();
|
||||||
|
player = xna::snew<PlatformerStarterKit::Player>(_this, start);
|
||||||
|
|
||||||
|
return Tile(nullptr, TileCollision::Passable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadExitTile(int x, int y) {
|
||||||
|
if (exit != InvalidPosition)
|
||||||
|
std::exception("A level may only have one exit.");
|
||||||
|
|
||||||
|
exit = GetBounds(x, y).Center();
|
||||||
|
|
||||||
|
return LoadTile("Exit", TileCollision::Passable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadEnemyTile(int x, int y, xna::String const& spriteSet) {
|
||||||
|
const auto position = RectangleExtensions::GetBottomCenter(GetBounds(x, y));
|
||||||
|
const auto _this = shared_from_this();
|
||||||
|
enemies.push_back(xna::snew<Enemy>(_this, position, spriteSet));
|
||||||
|
|
||||||
|
return Tile(nullptr, TileCollision::Passable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile Level::LoadGemTile(int x, int y) {
|
||||||
|
const auto position = GetBounds(x, y).Center();
|
||||||
|
const auto _this = shared_from_this();
|
||||||
|
gems.push_back(xna::snew<Gem>(_this, xna::Vector2(position.X, position.Y)));
|
||||||
|
|
||||||
|
return Tile(nullptr, TileCollision::Passable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::UpdateGems(xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < gems.size(); ++i) {
|
||||||
|
auto& gem = gems[i];
|
||||||
|
|
||||||
|
gem->Update(gameTime);
|
||||||
|
|
||||||
|
if (gem->BoundingCircle().Intersects(player->BoundingRectangle())) {
|
||||||
|
gems.erase(gems.begin() + i--);
|
||||||
|
OnGemCollected(gem, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::OnPlayerKilled(xna::sptr<Enemy> killedBy)
|
||||||
|
{
|
||||||
|
player->OnKilled(killedBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::UpdateEnemies(xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < enemies.size(); ++i) {
|
||||||
|
auto& enemy = enemies[i];
|
||||||
|
|
||||||
|
if (enemy->BoundingRectangle().Intersects(player->BoundingRectangle())) {
|
||||||
|
OnPlayerKilled(enemy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::OnExitReached()
|
||||||
|
{
|
||||||
|
player->OnReachedExit();
|
||||||
|
exitReachedSound->Play();
|
||||||
|
reachedExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::DrawTiles(xna::SpriteBatch& spriteBatch)
|
||||||
|
{
|
||||||
|
for (size_t y = 0; y < Height(); ++y) {
|
||||||
|
for (size_t x = 0; x < Width(); ++x) {
|
||||||
|
auto& texture = tiles[x][y].Texture;
|
||||||
|
if (texture) {
|
||||||
|
const auto p = xna::Vector2(x, y);
|
||||||
|
const auto s = xna::Vector2(x, y);
|
||||||
|
const auto position = p * s;
|
||||||
|
spriteBatch.Draw(texture, position, xna::Colors::White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::OnGemCollected(xna::sptr<Gem>& gem, xna::sptr<PlatformerStarterKit::Player>& collectedBy)
|
||||||
|
{
|
||||||
|
score += Gem::PointValue;
|
||||||
|
gem->OnCollected(collectedBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::Update(xna::GameTime const& gameTime) {
|
||||||
|
if (!player->IsAlive() || timeRemaining == xna::TimeSpan::Zero()) {
|
||||||
|
player->ApplyPhysics(gameTime);
|
||||||
|
}
|
||||||
|
else if (reachedExit) {
|
||||||
|
auto seconds = static_cast<int64_t>(std::round(gameTime.ElapsedGameTime.TotalSeconds() * 100.0f));
|
||||||
|
seconds = std::min(seconds, static_cast<int64_t>(std::ceil(timeRemaining.TotalSeconds())));
|
||||||
|
timeRemaining = timeRemaining - xna::TimeSpan::FromSeconds(seconds);
|
||||||
|
score += seconds * PointsPerSecond;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timeRemaining = timeRemaining - gameTime.ElapsedGameTime;
|
||||||
|
|
||||||
|
player->Update(gameTime);
|
||||||
|
|
||||||
|
UpdateGems(gameTime);
|
||||||
|
|
||||||
|
if (player->BoundingRectangle().Top() >= Height() * Tile::Height)
|
||||||
|
OnPlayerKilled(nullptr);
|
||||||
|
|
||||||
|
UpdateEnemies(gameTime);
|
||||||
|
|
||||||
|
if (player->IsAlive() &&
|
||||||
|
player->IsOnGround() &&
|
||||||
|
player->BoundingRectangle().Contains(exit)) {
|
||||||
|
OnExitReached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRemaining < xna::TimeSpan::Zero())
|
||||||
|
timeRemaining = xna::TimeSpan::Zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i <= EntityLayer; ++i)
|
||||||
|
spriteBatch.Draw(layers[i], xna::Vector2::Zero(), xna::Colors::White);
|
||||||
|
|
||||||
|
DrawTiles(spriteBatch);
|
||||||
|
|
||||||
|
/*foreach(Gem gem in gems)
|
||||||
|
gem.Draw(gameTime, spriteBatch);
|
||||||
|
|
||||||
|
Player.Draw(gameTime, spriteBatch);
|
||||||
|
|
||||||
|
foreach(Enemy enemy in enemies)
|
||||||
|
enemy.Draw(gameTime, spriteBatch);
|
||||||
|
|
||||||
|
for (int i = EntityLayer + 1; i < layers.Length; ++i)
|
||||||
|
spriteBatch.Draw(layers[i], Vector2.Zero, Color.White);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void Level::StartNewLife() {
|
||||||
|
player->Reset(start);
|
||||||
|
}
|
||||||
|
}
|
103
samples/02_PlatfformerStarterKit/level.hpp
Normal file
103
samples/02_PlatfformerStarterKit/level.hpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_LEVEL_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_LEVEL_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
#include "tile.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
|
||||||
|
class Player;
|
||||||
|
class Gem;
|
||||||
|
class Enemy;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A uniform grid of tiles with collections of gems and enemies.
|
||||||
|
* The level owns the player and controls the game's win and lose
|
||||||
|
* conditions as well as scoring.
|
||||||
|
*/
|
||||||
|
class Level : std::enable_shared_from_this<Level> {
|
||||||
|
public:
|
||||||
|
Level(xna::sptr<xna::IServiceProvider> const& serviceProvider, xna::String const& path );
|
||||||
|
|
||||||
|
public:
|
||||||
|
xna::sptr<PlatformerStarterKit::Player> Player() const;
|
||||||
|
|
||||||
|
constexpr int Score() const {
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool ReachedExit() const {
|
||||||
|
return reachedExit;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr xna::TimeSpan TimeRemaining() const {
|
||||||
|
return timeRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::sptr<xna::ContentManager> Content() const {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr xna::Rectangle GetBounds(int x, int y) const {
|
||||||
|
return xna::Rectangle(x * Tile::Width, y * Tile::Height, Tile::Width, Tile::Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr TileCollision GetCollision(int x, int y) const {
|
||||||
|
if (x < 0 || x >= Width())
|
||||||
|
return TileCollision::Impassable;
|
||||||
|
if (y < 0 || y >= Height())
|
||||||
|
return TileCollision::Passable;
|
||||||
|
|
||||||
|
return tiles[x][y].Collision;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int Width() const {
|
||||||
|
return tiles.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int Height() const {
|
||||||
|
return tiles[0].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(xna::GameTime const& gameTime);
|
||||||
|
void Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch);
|
||||||
|
void StartNewLife();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr xna::Point InvalidPosition = xna::Point(-1, -1);
|
||||||
|
static constexpr int PointsPerSecond = 5;
|
||||||
|
|
||||||
|
std::vector<std::vector<Tile>> tiles;
|
||||||
|
std::vector<xna::PTexture2D> layers;
|
||||||
|
static constexpr int EntityLayer = 2;
|
||||||
|
xna::sptr<PlatformerStarterKit::Player> player = nullptr;
|
||||||
|
std::vector<xna::sptr<Gem>> gems;
|
||||||
|
std::vector<xna::sptr<Enemy>> enemies;
|
||||||
|
|
||||||
|
xna::Vector2 start{};
|
||||||
|
xna::Point exit = InvalidPosition;
|
||||||
|
int score = 0;
|
||||||
|
bool reachedExit = false;
|
||||||
|
xna::TimeSpan timeRemaining{};
|
||||||
|
xna::sptr<xna::ContentManager> content = nullptr;
|
||||||
|
xna::sptr<xna::SoundEffect> exitReachedSound = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void LoadTiles(xna::String const& path);
|
||||||
|
Tile LoadTile(char tileType, int x, int y);
|
||||||
|
Tile LoadTile(xna::String const& name, TileCollision collision);
|
||||||
|
Tile LoadVarietyTile(xna::String const& baseName, int variationCount, TileCollision collision);
|
||||||
|
Tile LoadStartTile(int x, int y);
|
||||||
|
Tile LoadExitTile(int x, int y);
|
||||||
|
Tile LoadEnemyTile(int x, int y, xna::String const& spriteSet);
|
||||||
|
Tile LoadGemTile(int x, int y);
|
||||||
|
void UpdateGems(xna::GameTime const& gameTime);
|
||||||
|
void OnPlayerKilled(xna::sptr<Enemy> killedBy);
|
||||||
|
void UpdateEnemies(xna::GameTime const& gameTime);
|
||||||
|
void OnExitReached();
|
||||||
|
void DrawTiles(xna::SpriteBatch& spriteBatch);
|
||||||
|
void OnGemCollected(xna::sptr<Gem>& gem, xna::sptr<PlatformerStarterKit::Player>& collectedBy);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
228
samples/02_PlatfformerStarterKit/player.cpp
Normal file
228
samples/02_PlatfformerStarterKit/player.cpp
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
#include "player.hpp"
|
||||||
|
#include "level.hpp"
|
||||||
|
#include "enemy.hpp"
|
||||||
|
#include "extensions.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
Player::Player(xna::sptr<PlatformerStarterKit::Level> const& level, xna::Vector2 const& position)
|
||||||
|
: level(level)
|
||||||
|
{
|
||||||
|
LoadContent();
|
||||||
|
Reset(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::LoadContent()
|
||||||
|
{
|
||||||
|
idleAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Idle"), 0.1f, true);
|
||||||
|
runAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Run"), 0.1f, true);
|
||||||
|
jumpAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Jump"), 0.1f, false);
|
||||||
|
celebrateAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Celebrate"), 0.1f, false);
|
||||||
|
dieAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Die"), 0.1f, false);
|
||||||
|
|
||||||
|
const auto width = static_cast<int>(idleAnimation->FrameWidth() * 0.4);
|
||||||
|
const auto left = (idleAnimation->FrameWidth() - width) / 2;
|
||||||
|
const auto height = static_cast<int>(idleAnimation->FrameWidth() * 0.8);
|
||||||
|
const auto top = idleAnimation->FrameHeight() - height;
|
||||||
|
localBounds = xna::Rectangle(left, top, width, height);
|
||||||
|
|
||||||
|
killedSound = level->Content()->Load<xna::PSoundEffect>("Sounds/PlayerKilled");
|
||||||
|
jumpSound = level->Content()->Load<xna::PSoundEffect>("Sounds/PlayerJump");
|
||||||
|
fallSound = level->Content()->Load<xna::PSoundEffect>("Sounds/PlayerFall");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::Reset(xna::Vector2 const& position)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
Velocity = xna::Vector2::Zero();
|
||||||
|
isAlive = true;
|
||||||
|
sprite->PlayAnimation(idleAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::ApplyPhysics(xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
float elapsed = static_cast<float>(gameTime.ElapsedGameTime.TotalSeconds());
|
||||||
|
|
||||||
|
auto previousPosition = Position;
|
||||||
|
|
||||||
|
Velocity.X += movement * MoveAcceleration * elapsed;
|
||||||
|
Velocity.Y = xna::MathHelper::Clamp(Velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);
|
||||||
|
|
||||||
|
Velocity.Y = DoJump(Velocity.Y, gameTime);
|
||||||
|
|
||||||
|
if (IsOnGround())
|
||||||
|
Velocity.X *= GroundDragFactor;
|
||||||
|
else
|
||||||
|
Velocity.X *= AirDragFactor;
|
||||||
|
|
||||||
|
Velocity.X = xna::MathHelper::Clamp(Velocity.X, -MaxMoveSpeed, MaxMoveSpeed);
|
||||||
|
|
||||||
|
Position = Position + (Velocity * elapsed);
|
||||||
|
Position = xna::Vector2(std::round(Position.X), std::round(Position.Y));
|
||||||
|
|
||||||
|
HandleCollisions();
|
||||||
|
|
||||||
|
if (Position.X == previousPosition.X)
|
||||||
|
Velocity.X = 0;
|
||||||
|
|
||||||
|
if (Position.Y == previousPosition.Y)
|
||||||
|
Velocity.Y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::Update(xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
GetInput();
|
||||||
|
|
||||||
|
ApplyPhysics(gameTime);
|
||||||
|
|
||||||
|
if (IsAlive() && IsOnGround()) {
|
||||||
|
if (std::abs(Velocity.X) - 0.02f > 0) {
|
||||||
|
sprite->PlayAnimation(runAnimation);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprite->PlayAnimation(idleAnimation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
movement = 0.0f;
|
||||||
|
isJumping = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::GetInput()
|
||||||
|
{
|
||||||
|
auto gamePadState = xna::GamePad::GetState(xna::PlayerIndex::One);
|
||||||
|
auto keyboardState = xna::Keyboard::GetState();
|
||||||
|
|
||||||
|
movement = gamePadState.ThumbSticks.Left().X * MoveStickScale;
|
||||||
|
|
||||||
|
if (std::abs(movement) < 0.5f)
|
||||||
|
movement = 0.0f;
|
||||||
|
|
||||||
|
if (gamePadState.IsButtonDown(xna::Buttons::DPadLeft) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::Left) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::A)) {
|
||||||
|
movement = -1.0f;
|
||||||
|
}
|
||||||
|
else if (gamePadState.IsButtonDown(xna::Buttons::DPadRight) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::Right) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::D)) {
|
||||||
|
movement = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
isJumping =
|
||||||
|
gamePadState.IsButtonDown(JumpButton) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::Space) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::Up) ||
|
||||||
|
keyboardState.IsKeyDown(xna::Keys::W);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::OnKilled(xna::sptr<Enemy>& killedBy)
|
||||||
|
{
|
||||||
|
isAlive = false;
|
||||||
|
|
||||||
|
if (!killedBy)
|
||||||
|
killedSound->Play();
|
||||||
|
else
|
||||||
|
fallSound->Play();
|
||||||
|
|
||||||
|
sprite->PlayAnimation(dieAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch)
|
||||||
|
{
|
||||||
|
if (Velocity.X > 0)
|
||||||
|
flip = xna::SpriteEffects::FlipHorizontally;
|
||||||
|
else if (Velocity.X < 0)
|
||||||
|
flip = xna::SpriteEffects::None;
|
||||||
|
|
||||||
|
sprite->Draw(gameTime, spriteBatch, Position, flip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::OnReachedExit()
|
||||||
|
{
|
||||||
|
sprite->PlayAnimation(celebrateAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Player::DoJump(float velocityY, xna::GameTime const& gameTime)
|
||||||
|
{
|
||||||
|
if (isJumping) {
|
||||||
|
if ((!wasJumping && IsOnGround()) || jumpTime > 0.0f) {
|
||||||
|
if (jumpTime == 0.0f)
|
||||||
|
jumpSound->Play();
|
||||||
|
|
||||||
|
jumpTime += static_cast<float>(gameTime.ElapsedGameTime.TotalSeconds());
|
||||||
|
sprite->PlayAnimation(jumpAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0.0f < jumpTime && jumpTime <= MaxJumpTime) {
|
||||||
|
velocityY = JumpLaunchVelocity * (1.0f - static_cast<float>(std::pow(jumpTime / MaxJumpTime, JumpControlPower)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jumpTime = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jumpTime = 0.0f;
|
||||||
|
}
|
||||||
|
wasJumping = isJumping;
|
||||||
|
|
||||||
|
return velocityY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::HandleCollisions()
|
||||||
|
{
|
||||||
|
auto bounds = BoundingRectangle();
|
||||||
|
auto leftTile = std::floor(static_cast<float>(bounds.Left()) / Tile::Width);
|
||||||
|
auto rightTile = std::ceil((static_cast<float>(bounds.Right()) / Tile::Width)) - 1;
|
||||||
|
auto topTile = std::floor(static_cast<float>(bounds.Top()) / Tile::Height);
|
||||||
|
auto bottomTile = std::ceil((static_cast<float>(bounds.Bottom()) / Tile::Height)) - 1;
|
||||||
|
|
||||||
|
isOnGround = false;
|
||||||
|
|
||||||
|
for (size_t y = topTile; y <= bottomTile; ++y) {
|
||||||
|
for (size_t x = leftTile; x <= rightTile; ++x) {
|
||||||
|
auto collision = level->GetCollision(x, y);
|
||||||
|
if (collision != TileCollision::Passable) {
|
||||||
|
auto tileBounds = level->GetBounds(x, y);
|
||||||
|
auto depth = RectangleExtensions::GetIntersectionDepth(bounds, tileBounds);
|
||||||
|
if (depth != xna::Vector2::Zero()) {
|
||||||
|
auto absDepthX = std::abs(depth.X);
|
||||||
|
auto absDepthY = std::abs(depth.Y);
|
||||||
|
|
||||||
|
if (absDepthY < absDepthX || collision == TileCollision::Platform) {
|
||||||
|
if (previousBottom <= tileBounds.Top())
|
||||||
|
isOnGround = true;
|
||||||
|
|
||||||
|
if (collision == TileCollision::Impassable || IsOnGround()) {
|
||||||
|
Position = xna::Vector2(Position.X, Position.Y + depth.Y);
|
||||||
|
bounds = BoundingRectangle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (collision == TileCollision::Impassable)
|
||||||
|
{
|
||||||
|
Position = xna::Vector2(Position.X + depth.X, Position.Y);
|
||||||
|
bounds = BoundingRectangle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousBottom = bounds.Bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> Player::Level() const
|
||||||
|
{
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::Rectangle Player::BoundingRectangle() const {
|
||||||
|
auto left = std::round(Position.X - sprite->Origin().X) + localBounds.X;
|
||||||
|
auto top = std::round(Position.Y - sprite->Origin().Y) + localBounds.Y;
|
||||||
|
|
||||||
|
return xna::Rectangle(
|
||||||
|
static_cast<int>(left),
|
||||||
|
static_cast<int>(top),
|
||||||
|
localBounds.Width,
|
||||||
|
localBounds.Height);
|
||||||
|
}
|
||||||
|
}
|
80
samples/02_PlatfformerStarterKit/player.hpp
Normal file
80
samples/02_PlatfformerStarterKit/player.hpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_PLAYER_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_PLAYER_HPP
|
||||||
|
|
||||||
|
#include "xna.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
class Level;
|
||||||
|
class Enemy;
|
||||||
|
|
||||||
|
// Our fearless adventurer!
|
||||||
|
class Player {
|
||||||
|
public:
|
||||||
|
Player(xna::sptr<Level> const& level, xna::Vector2 const& position);
|
||||||
|
|
||||||
|
public:
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> Level() const;
|
||||||
|
|
||||||
|
constexpr bool IsAlive() const {
|
||||||
|
return isAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsOnGround() const {
|
||||||
|
return isOnGround;
|
||||||
|
}
|
||||||
|
|
||||||
|
xna::Rectangle BoundingRectangle() const;
|
||||||
|
|
||||||
|
void LoadContent();
|
||||||
|
void Reset(xna::Vector2 const& position);
|
||||||
|
void ApplyPhysics(xna::GameTime const& gameTime);
|
||||||
|
void Update(xna::GameTime const& gameTime);
|
||||||
|
void GetInput();
|
||||||
|
void OnKilled(xna::sptr<Enemy>& killedBy);
|
||||||
|
void Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch);
|
||||||
|
void OnReachedExit();
|
||||||
|
|
||||||
|
public:
|
||||||
|
xna::Vector2 Position{};
|
||||||
|
xna::Vector2 Velocity{};
|
||||||
|
|
||||||
|
private:
|
||||||
|
xna::sptr<Animation> idleAnimation = nullptr;
|
||||||
|
xna::sptr<Animation> runAnimation = nullptr;
|
||||||
|
xna::sptr<Animation> jumpAnimation = nullptr;
|
||||||
|
xna::sptr<Animation> celebrateAnimation = nullptr;
|
||||||
|
xna::sptr<Animation> dieAnimation = nullptr;
|
||||||
|
xna::SpriteEffects flip = xna::SpriteEffects::None;
|
||||||
|
xna::sptr<AnimationPlayer> sprite = nullptr;
|
||||||
|
xna::sptr<xna::SoundEffect> killedSound = nullptr;
|
||||||
|
xna::sptr<xna::SoundEffect> jumpSound = nullptr;
|
||||||
|
xna::sptr<xna::SoundEffect> fallSound = nullptr;
|
||||||
|
xna::sptr<PlatformerStarterKit::Level> level = nullptr;
|
||||||
|
bool isAlive = false;
|
||||||
|
float previousBottom = 0.0F;
|
||||||
|
bool isOnGround = false;
|
||||||
|
float movement = 0.0f;
|
||||||
|
bool isJumping = false;
|
||||||
|
bool wasJumping = false;
|
||||||
|
float jumpTime = false;
|
||||||
|
xna::Rectangle localBounds{};
|
||||||
|
|
||||||
|
static constexpr float MoveAcceleration = 14000.0f;
|
||||||
|
static constexpr float MaxMoveSpeed = 2000.0f;
|
||||||
|
static constexpr float GroundDragFactor = 0.58f;
|
||||||
|
static constexpr float AirDragFactor = 0.65f;
|
||||||
|
static constexpr float MaxJumpTime = 0.35f;
|
||||||
|
static constexpr float JumpLaunchVelocity = -4000.0f;
|
||||||
|
static constexpr float GravityAcceleration = 3500.0f;
|
||||||
|
static constexpr float MaxFallSpeed = 600.0f;
|
||||||
|
static constexpr float JumpControlPower = 0.14f;
|
||||||
|
static constexpr float MoveStickScale = 1.0f;
|
||||||
|
static constexpr xna::Buttons JumpButton = xna::Buttons::A;
|
||||||
|
|
||||||
|
float DoJump(float velocityY, xna::GameTime const& gameTime);
|
||||||
|
void HandleCollisions();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
28
samples/02_PlatfformerStarterKit/tile.hpp
Normal file
28
samples/02_PlatfformerStarterKit/tile.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef PLATFORMSTARTERKIT_TILE_HPP
|
||||||
|
#define PLATFORMSTARTERKIT_TILE_HPP
|
||||||
|
|
||||||
|
namespace PlatformerStarterKit {
|
||||||
|
// Controls the collision detection and response behavior of a tile.
|
||||||
|
enum class TileCollision {
|
||||||
|
Passable = 0,
|
||||||
|
Impassable = 1,
|
||||||
|
Platform = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Tile {
|
||||||
|
xna::PTexture2D Texture = nullptr;
|
||||||
|
TileCollision Collision{};
|
||||||
|
constexpr static int Width = 64;
|
||||||
|
constexpr static int Height = 48;
|
||||||
|
|
||||||
|
constexpr static xna::Vector2 Size() {
|
||||||
|
return{ Width, Height };
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Tile() = default;
|
||||||
|
Tile(xna::PTexture2D const& texture, TileCollision collision) :
|
||||||
|
Texture(texture), Collision(collision) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -3,4 +3,4 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Add source to this project's executable.
|
# Add source to this project's executable.
|
||||||
add_subdirectory ("01_blank")
|
add_subdirectory ("02_PlatfformerStarterKit")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user