diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a8bc10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +build \ No newline at end of file diff --git a/assets/C64.ttf b/assets/C64.ttf new file mode 100644 index 0000000..02ff5fc Binary files /dev/null and b/assets/C64.ttf differ diff --git a/assets/sprites.bmp b/assets/sprites.bmp new file mode 100644 index 0000000..0fbd794 Binary files /dev/null and b/assets/sprites.bmp differ diff --git a/include/input.h b/include/input.h new file mode 100644 index 0000000..bab3c4b --- /dev/null +++ b/include/input.h @@ -0,0 +1,14 @@ +#include +#include +#include + +class Input +{ + private: + SDL_Event event; + + public: + bool exit; + std::map activeKeys; + void update(); +}; \ No newline at end of file diff --git a/include/sprite.h b/include/sprite.h new file mode 100644 index 0000000..d0b3f5b --- /dev/null +++ b/include/sprite.h @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include "vector.h" +#pragma once + +class Sprite +{ + private: + std::map frames; + SDL_Surface* surface; + SDL_Texture* Texture; + int_vec2 size; + + public: + SDL_Rect dstrect; + int_vec2 scale = { 1, 1 }; + double angle = 0; + Sprite(SDL_Renderer* renderer, SDL_Surface* surface); + void draw(SDL_Renderer* renderer, std::string frame, bool autoRect = true, bool flip = false); + void addFrame(std::string name, SDL_Rect rect); + void move(int_vec2 position); + int_vec2 getSize(std::string frame); + void autorect(std::string frame); + ~Sprite(); +}; + +class Animation +{ + private: + std::vector frames; + Sprite* sprite; + int currentFrame = 0; + int frameDuration = 0; + int frameCounter = 0; + std::string currentFrameName; + int frameIndex = 0; + int frameDirection = 1; + + public: + bool loop = true; + bool pingpong = false; + Animation(std::map frames, Sprite* sprite, int frameDuration); + void update(); + void draw(SDL_Renderer* renderer, bool autoRect = true, bool flip = false); +}; + +class AnimationManager +{ + private: + std::map *animations; + std::map flip; + std::map autoRect; + std::string currentAnimation; + Sprite* sprite; + + public: + AnimationManager(Sprite* sprite); + void addAnimation(std::string name, std::map animation, int frameDuration = 10, bool flip = false, bool autoRect = true, bool loop = true, bool pingpong = false); + void setAnimation(std::string name); + void update(); + void draw(SDL_Renderer* renderer); +}; \ No newline at end of file diff --git a/include/text.h b/include/text.h new file mode 100644 index 0000000..33f3fe9 --- /dev/null +++ b/include/text.h @@ -0,0 +1,17 @@ +#include +#include +#include + +class Text +{ +private: + SDL_Surface* surface; + SDL_Texture* texture; + TTF_Font* font; + SDL_Color color; + SDL_Rect rect; + +public: + Text(TTF_Font* font, SDL_Color color); + void draw(SDL_Renderer* renderer, std::string text, int x, int y); +}; diff --git a/include/vector.h b/include/vector.h new file mode 100644 index 0000000..8f660f6 --- /dev/null +++ b/include/vector.h @@ -0,0 +1,49 @@ +#pragma once + +class float_vec2 +{ + public: + float x, y; + float_vec2 operator+(float_vec2 vec) + { + return { x + vec.x, y + vec.y }; + } + float_vec2 operator*(float v) + { + return { x * v, y * v }; + } + float_vec2 operator*(float_vec2 vec) + { + return { x * vec.x, y * vec.y }; + } + float_vec2 operator-(float_vec2 vec) + { + return { x - vec.x, y - vec.y }; + } + float_vec2 operator-(float v) + { + return { x - v, y - v }; + } +}; + +class int_vec2 +{ + public: + int x, y; + int_vec2 operator+(int_vec2 vec) + { + return { x + vec.x, y + vec.y }; + } + int_vec2 operator*(int v) + { + return { x * v, y * v }; + } + int_vec2 operator*(int_vec2 vec) + { + return { x * vec.x, y * vec.y }; + } + int_vec2 operator-(int_vec2 vec) + { + return { x - vec.x, y - vec.y }; + } +}; \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..7f45a21 --- /dev/null +++ b/makefile @@ -0,0 +1,17 @@ +CXX = g++ +LDFLAGS = `sdl2-config --libs` -l SDL2_ttf +CXXFLAGS = `sdl2-config --cflags` -pedantic -O2 -std=c++17 -lX11 -lstdc++fs -I include +SRC = $(wildcard src/*.cpp) +OBJ = $(addprefix build/, $(notdir $(SRC:.cpp=.o))) +TARGET = build/x86_64/main.x86_64 + +all: $(TARGET) + +$(TARGET): $(OBJ) + @$(CXX) -o $@ $^ $(LDFLAGS) + +build/%.o: src/%.cpp + @$(CXX) -c -o $@ $< $(CXXFLAGS) + +clean: + @rm -f $(OBJ) $(TARGET) \ No newline at end of file diff --git a/src/input.cpp b/src/input.cpp new file mode 100644 index 0000000..eceb96a --- /dev/null +++ b/src/input.cpp @@ -0,0 +1,20 @@ +#include "input.h" + +void Input::update() +{ + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_KEYDOWN) + { + activeKeys[event.key.keysym.sym] = true; + } + else if (event.type == SDL_KEYUP) + { + activeKeys.erase(event.key.keysym.sym); + } + else if (event.type == SDL_QUIT) + { + exit = true; + } + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4b5c26d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,95 @@ +// Inludes +#include +#include +#include +#include "input.h" +#include "text.h" +#include "sprite.h" + +// Variables +SDL_Surface* assets; +int startTime = 0; +Input input; +TTF_Font *font; +SDL_Window *window; +SDL_Renderer *renderer; +const int targetFps = 60; + +// Functions +// Quit function +void quit() +{ + // Free assets + TTF_CloseFont(font); + SDL_FreeSurface(assets); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + TTF_Quit(); + // Exit program + exit(0); +} + +// Main function +int main(int argc, char* argv[]) +{ + // Initialize SDL and TFF; + TTF_Init(); + SDL_Init(SDL_INIT_VIDEO); + + // Create window and renderer + window = SDL_CreateWindow("test game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 600, 400, SDL_WINDOW_SHOWN); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + // Load assets + assets = SDL_LoadBMP("assets/sprites.bmp"); + + // frame map + std::map frames = { + { "idle", { 4, 456, 68, 125 } }, + { "idle2", { 76, 456, 68, 128 } }, + { "idle3", { 148, 456, 68, 125 } } + }; + + // Create sprite and animation + Sprite* sprite = new Sprite(renderer, assets); + AnimationManager* animations = new AnimationManager(sprite); + animations->addAnimation("idle", frames); + animations->setAnimation("idle"); + + // Main loop + while (true) + { + // Start time + startTime = SDL_GetTicks(); + + // Update input + input.update(); + + // Exit if window is closed + if (input.exit) + { + quit(); + } + + // Update animation + animations->update(); + + // Clear screen + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // Draw sprite + animations->draw(renderer); + + // Update screen + SDL_RenderPresent(renderer); + + // if frame is faster than target fps, delay + if (1000 / targetFps > SDL_GetTicks() - startTime) + { + SDL_Delay(1000 / targetFps - (SDL_GetTicks() - startTime)); + } + } + return 0; +} \ No newline at end of file diff --git a/src/sprite.cpp b/src/sprite.cpp new file mode 100644 index 0000000..87be1c0 --- /dev/null +++ b/src/sprite.cpp @@ -0,0 +1,126 @@ +#include "sprite.h" + +Sprite::Sprite(SDL_Renderer *renderer, SDL_Surface *surface) +{ + Texture = SDL_CreateTextureFromSurface(renderer, surface); + dstrect = { 0, 0, surface->w, surface->h }; +} + +Sprite::~Sprite() +{ + SDL_DestroyTexture(Texture); +} + +void Sprite::draw(SDL_Renderer* renderer, std::string frame, bool autoRect, bool flip) + { + if (frames.find(frame) == frames.end()) + { + printf("Frame not found\n"); + return; + } + if (autoRect) + { + dstrect = { dstrect.x, dstrect.y, frames[frame].w, frames[frame].h }; + } + SDL_SetTextureAlphaMod(Texture, 255); + dstrect.w *= scale.x; + dstrect.h *= scale.y; + SDL_RenderCopyEx(renderer, Texture, &frames[frame], &dstrect, angle, NULL, flip ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE); +} + +void Sprite::addFrame(std::string name, SDL_Rect rect) +{ + frames[name] = rect; +} + +void Sprite::move(int_vec2 position) +{ + dstrect = { position.x, position.y, dstrect.w, dstrect.h }; +} + +int_vec2 Sprite::getSize(std::string frame) +{ + size = { frames[frame].w, frames[frame].h }; + return size; +} + +void Sprite::autorect(std::string frame) +{ + dstrect = { dstrect.x, dstrect.y, frames[frame].w, frames[frame].h }; +} + + +Animation::Animation(std::map frames, Sprite* sprite, int frameDuration) +{ + this->sprite = sprite; + this->frameDuration = frameDuration; + + for (auto const& x : frames) + { + this->frames.push_back(x.first); + this->sprite->addFrame(x.first, x.second); + } + currentFrameName = this->frames[0]; +} + +void Animation::update() +{ + frameCounter++; + if (frameCounter >= frameDuration) + { + frameCounter = 0; + if (pingpong) + { + frameIndex += frameDirection; + if (frameIndex >= frames.size() - 1 || frameIndex <= 0) + { + frameDirection *= -1; + } + } + else + { + frameIndex++; + if (frameIndex >= frames.size()) + { + frameIndex = 0; + } + } + + + currentFrameName = frames[frameIndex]; + } +} + +void Animation::draw(SDL_Renderer* renderer, bool autoRect, bool flip) +{ + sprite->draw(renderer, currentFrameName, autoRect, flip); +} + +AnimationManager::AnimationManager(Sprite* sprite) +{ + this->sprite = sprite; +} + +void AnimationManager::addAnimation(std::string name, std::map frames, int frameDuration, bool flip, bool autoRect, bool loop, bool pingpong) +{ + animations[name] = new Animation(frames, sprite, frameDuration); + this->flip[name] = flip; + this->autoRect[name] = autoRect; + animations[name].loop = loop; + animations[name].pingpong = pingpong; +} + +void AnimationManager::setAnimation(std::string name) +{ + currentAnimation = name; +} + +void AnimationManager::update() +{ + animations[currentAnimation].update(); +} + +void AnimationManager::draw(SDL_Renderer* renderer) +{ + animations[currentAnimation].draw(renderer, autoRect[currentAnimation], flip[currentAnimation]); +} \ No newline at end of file diff --git a/src/text.cpp b/src/text.cpp new file mode 100644 index 0000000..71d8cdd --- /dev/null +++ b/src/text.cpp @@ -0,0 +1,17 @@ +#include "text.h" + +Text::Text(TTF_Font* font, SDL_Color color) +{ + this->font = font; + this->color = color; +} + +void Text::draw(SDL_Renderer* renderer, std::string text, int x, int y) +{ + surface = TTF_RenderText_Solid(font, text.c_str(), color); + texture = SDL_CreateTextureFromSurface(renderer, surface); + rect = { x, y, surface->w, surface->h }; + SDL_RenderCopy(renderer, texture, NULL, &rect); + SDL_FreeSurface(surface); + SDL_DestroyTexture(texture); +} \ No newline at end of file