Giter Club home page Giter Club logo

cherrysoda-engine's Introduction

CherrySoda Engine

A lightweight cross-platform C++ game engine based on bgfx and SDL2

Introduction

CherrySoda engine is a C++ port of Monocle engine, with some changes and additional functionalities.

It's a hobby project, I used it to participate in several gamejams.

Demos

https://fountainment.github.io/demo/

API List

https://fountainment.github.io/doc/

Monocle engine UML: monocle-engine.png

Requirements

  • CMake >=3.12
  • Python >=3.6

Windows

  • Visual Studio >=2017 or MinGW(GCC >= 5.2)

Linux

  • GCC >=5.2 or Clang >=3.4
  • libSDL2-dev

MacOS

  • brew install sdl2

Current Features

  • Features Ported from Monocle Engine:
    • simple entity-component-system
    • simple tag-based renderer
    • simple tag-based 2d collision detection
    • 2d camera
    • 2d sprite animation system
    • 2d cpu particle system
    • bmfont file loading and pixel font rendering
    • ease functions
    • some useful components (Alarm, StateMachine, Tween, ...)
    • tile-based game utilities (TileSet, TileGrid, Grid)
  • Additional Features
    • command console powered by ImGui
    • simple audio playback (support wav and ogg loading)
    • gltf file loading (only mesh data)
    • mesh rendering
    • html5 support
  • Tools:
    • crunch (a texture packer) (modified) (MIT Licence)
    • sfxr (an 8-bit sound effect generator) (ported) (MIT Licence)
    • particle effect editor

cherrysoda-engine's People

Contributors

fountainment avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

cherrysoda-engine's Issues

Sprite animations created from Atlas

Hi, I´ve encountered a minor issue which I try to solve and would like to consult you before trying to change the source code locally.

What I´m basically trying to do is, having created a huge atlas of sprites with crunch, to create an animated sprite from that atlas by subsequently specifying the sub-textures for each frame (an example down below).

Currently we animate a sprite created with crunch as follows,
given the definition in JSON

{
	"textures":[
		{
			"name":"King",
			"images":[
				{ "n":"tile0", "x":0, "y":42, "w":12, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile4", "x":0, "y":0, "w":13, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile3", "x":0, "y":14, "w":13, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile1", "x":0, "y":28, "w":12, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile2", "x":0, "y":56, "w":12, "h":12, "fx":-9, "fy":-19, "fw":32, "fh":32 }
			]
		}
	]
}

we do in .cpp

entity = factory->Begin(this)
		.Add(new  Sprite("assets/King.json"))
		.End();

	entity->Get< Sprite >()->AddLoop("Idle", "tile", 0.16f, { 0, 3, 2, 4, 1 }); // Set corresponding animation frames. Must match JSON file.

However, this implies we must have created a JSON file with crunch for every animated sprite (as to my understanding).

How can we achieve the following,
given the JSON

{
	"textures":[
		{
			"name":"UnitAtlas0",
			"images":[
				{ "n":"HighElf_Knight_A", "x":0, "y":0, "w":112, "h":120, "fx":-72, "fy":-48, "fw":256, "fh":256 },
				{ "n":"HighElf_Knight_B", "x":0, "y":121, "w":112, "h":120, "fx":-72, "fy":-48, "fw":256, "fh":256 },
				{ "n":"Human_Warrior", "x":0, "y":242, "w":112, "h":120, "fx":-72, "fy":-48, "fw":256, "fh":256 }
                // ...

                   ]
                }
}

we do in .cpp

entity = factory->Begin(this)
		.Add(new  Sprite("assets/UnitAtlas0.json"))
		.End();

	entity->Get< Sprite >()->AddToLoop("Idle", "HighElf_Knight_A"); // We choose a specific sub-texture for the next frame matching the name in JSON.
	entity->Get< Sprite >()->AddToLoop("Idle", "HighElf_Knight_B");

Building on Linux (Ubuntu)

I have issues building on Ubuntu Linux.

As I run the command "cmake --build . --config Release" it builds up to 43% and in the file "Interface/Window.cpp" it says "union SDL_SysWMinfo has no member named 'x11'" at 415
return (void*)wmi.info.x11.window;
and at 446
return (void*)wmi.info.x11.display;

Beforehand I have installed needed libraries as specified in the workflow file "linux.yml".

I tried to resolve the issues for the last two days now but nothing helps, maybe You have an idea.

Add XML in Atlas::ReadAtlasData(...)

Greetings, it would be nice if You could include loading for the Atlas from XML. Unfortunately I lack knowledge of cmake to create a clean PR, so here is my implementation:

XML.h

#ifndef _CHERRYSODA_UTIL_XML_H_
#define _CHERRYSODA_UTIL_XML_H_
#pragma once

#include <CherrySoda/Util/String.h>

#include <tinyxml/tinyxml2>

namespace cherrysoda
{
	namespace XML
	{
		using namespace tinyxml2;

	} // namespace tinyxml2


	class XMLUtil
	{
	public:
		static bool ReadXMLFile(cherrysoda::XML::XMLDocument& doc, const String& filename);
	};

} // namespace cherrysoda


#endif // _CHERRYSODA_UTIL_XML_H_

XML.cpp

#include "XML.h"

bool cherrysoda::XMLUtil::ReadXMLFile(cherrysoda::XML::XMLDocument& doc, const cherrysoda::String& filename)
{
	cherrysoda::XML::XMLError result = doc.LoadFile(filename.c_str());
	if (result != cherrysoda::XML::XMLError::XML_SUCCESS)
	{
		// Clean up.
		doc.Clear();
		return false;
	}

	return true;
}

Atlas.h

... previous code
	enum class AtlasDataFormat
	{
		CrunchJson,
		CrunchBinary,
		CrunchJsonOrBinary,
		CrunchXML, // NEW
		CrunchBinaryNoAtlas,
		Packer,
		PackerNoAtlas,
	};
... old code remain

Atlas.cpp

void Atlas::ReadAtlasData(Atlas* atlas, const String& path, AtlasDataFormat format/* = AtlasDataFormat::CrunchJson*/)
{

	cherrysoda::json::Document doc;
	cherrysoda::XML::XMLDocument xmlDoc;

	switch (format) {
	case AtlasDataFormat::CrunchJson:
	{
		JsonUtil::ReadJsonFile(doc, path);
		const auto& at = doc["textures"];
		CHERRYSODA_ASSERT_FORMAT(at.IsArray(), "Atlas json parse failed: \"textures\" scope is not an array in \"%s\"!\n");
		for (const auto& tex : at.GetArray()) {
			String texturePath = StringUtil::Path_GetDirectoryName(path) + tex["name"].GetString() + ".png";
			auto texture = Texture2D::FromFile(texturePath);
			auto mTexture = MTexture(texture);
			STL::Add(atlas->m_sources, texture);
			const auto& img = tex["images"];
			CHERRYSODA_ASSERT_FORMAT(img.IsArray(), "Atlas json parse failed: \"images\" scope is not an array in \"%s\"!\n");
			for (const auto& sub : img.GetArray()) {
				const char* name = sub["n"].GetString();
				auto clipRect = Math::IRectangle{
					Math::IVec2(sub["x"].GetInt(), sub["y"].GetInt()),
					Math::IVec2(sub["w"].GetInt(), sub["h"].GetInt()),
				};
				if (sub.HasMember("fx")) {
					atlas->m_textures[name] = MTexture(mTexture, name, clipRect, Math::Vec2(-sub["fx"].GetInt(), -sub["fy"].GetInt()), sub["fw"].GetInt(), sub["fh"].GetInt());
				}
				else {
					atlas->m_textures[name] = MTexture(mTexture, name, clipRect);
				}
			}
		}
		break;
	}
	case AtlasDataFormat::CrunchXML:

		if (XMLUtil::ReadXMLFile(xmlDoc, path))
		{
			tinyxml2::XMLNode* root = nullptr;
			root = xmlDoc.FirstChild();
			if (root != nullptr)
			{
				const char* name = root->FirstChildElement("tex")->Attribute("n");
				String texturePath = StringUtil::Path_GetDirectoryName(path) + String(name) + ".png";

				auto texture = Texture2D::FromFile(texturePath);
				auto mTexture = MTexture(texture);

				STL::Add(atlas->m_sources, texture);

				tinyxml2::XMLElement* img = root->FirstChildElement("tex")->FirstChildElement("img");
				while (img)
				{
					const char* sub_name = img->Attribute("n");
					auto clipRect = Math::IRectangle{
						Math::IVec2(img->IntAttribute("x"), img->IntAttribute("y")),
						Math::IVec2(img->IntAttribute("w"), img->IntAttribute("h")),
					};

					atlas->m_textures[name] = MTexture(mTexture, name, clipRect, Math::Vec2(-img->IntAttribute("fx"), -img->IntAttribute("fy")), img->IntAttribute("fw"), img->IntAttribute("fh"));

					img = img->NextSiblingElement("img");
				}
			}

			xmlDoc.Clear();
			break;
		}

		break;
	default:
		CHERRYSODA_ASSERT(false, "Atlas data format unsupported for now!\n");
		break;
	}
}

Event System

Hello Evan, have you thought about adding an event system for the engine? I did not see an event system in the Monocle Engine, so maybe this would be a handy improvement.

In my build I´ve integrated the event system, event emitter and event listener as components and plan to finish and test it later some time.

Debug Console Behavior

It would be nice to be able to toggle the GUI ImGui console based on input in a scene.
For example:

void Scene::Update()
{
	if (MInput::Keyboard()->Pressed(Keys::OemTilde))
	{
		Engine::ToggleConsole();
	}
}

Currently the function "ToggleConsole" is private, my proposition would be to make it protected.

Reason for that could be own implementation of a debug console, as it is in my case. And the only possibility I saw to disable the debug console was GUI::Disable();, which in turn disabled my own implementation too... for that I could not see any solution other than mentioned above.

Animations

Could you please elaborate on how to create animations with the current crunch import format?! I dont seem to get it.

Suppose we have a png file with 5 frames. Let the json atlas be as follows

{
	"textures":[
		{
			"name":"King",
			"images":[
				{ "n":"tile009", "x":0, "y":42, "w":12, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile013", "x":0, "y":0, "w":13, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile012", "x":0, "y":14, "w":13, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile010", "x":0, "y":28, "w":12, "h":13, "fx":-9, "fy":-18, "fw":32, "fh":32 },
				{ "n":"tile011", "x":0, "y":56, "w":12, "h":12, "fx":-9, "fy":-19, "fw":32, "fh":32 }
			]
		}
	]
}

In our code we create an Entity with a sprite component

auto entity = factory->Begin( scene )  
		.Add(new  Sprite("assets/King.json"))  
		.End();

Lastly we define (following the example "FirstSprite")

	entity->Get< Sprite >()->AddLoop("Idle", "tile009");
	entity->Get< Sprite >()->Play("Idle");
	entity->Get< Sprite >()->Position2D(Math::Vec2(100.0f, 100.0f));
	entity->Get< Sprite >()->CenterOrigin();

This renders as expected only the Texture named "tile009". However, the goal is to render the images in order like
"tile009"->"tile013"->"tile012"->"tile010"->"tile011".
Naively specifying

entity->Get< Sprite >()->AddLoop("Idle", "King");

does not work either.
So I wonder. How am I supposed to create animations?

Thank You in advance!

Implement SpriteBank Editor

SpriteBank uses json that needs to be written by hand.
It's not very user-friendly, because one can often miss comma or add surplus comma,
which make json validation failed.

An GUI editor may make the process much more convenient.

Add Wayland Support

For display server on Linux, seems that X11 is becoming the past, and Wayland is the future.
According to some news I saw, Ubuntu is switching to use Wayland by default, and it might be in the next comming LTS version.

If you run "echo $XDG_SESSION_TYPE", it shows "wayland", it means currently this engine may not compile or work on your system.

Seems it's time to add wayland support. I think the most job had already being done by bgfx. So the work amount shouldn't be much. I will start looking at this issue after I choose some time to establish a test environment.

ImGui

I observed an issue with ImGui.
The following code

ImGui::Begin("Test"); ImGui::Text("Hello World"); ImGui::End();

creates an ImGui window as normal, but writes the text twice. The same duplication is observed with everything within the ImGui windows.

The code is inside a scene update function. And Im not sure whether my code is not in the intended place or there is a bug.

Nice Project

Man, first, this is a nice project.

This can be called a complete engine (with a basic 2d engine features)?

I want know too if it run on iOS/Android.

Thanks.

Layered Drawing 2D

I´ve encountered a problem trying rendering sprites over each other.
I render two sprites as follows

first->Position(Math::Vec3(0.0f, 0.0f, 10.0f));
second->Position2D(Math::Vec2(0.0f, 0.0f));

and I expect the first to be drawn above the second (changing position of first to Math::Vec3(0.0f, 0.0f, -10.0f); does not change anything). However, they are drawn in the order they were instantiated. Thus creating the second as first sprite and the first sprite as second yields the desired result.

The renderer is copy-pasted from example code

cherrysoda::Graphics::SetPointTextureSampling();
auto renderer = new cherrysoda::EverythingRenderer();
auto camera = renderer->GetCamera();
camera->Position(cherrysoda::Math::Vec3(0.0f, 0.0f, 200.0f));
renderer->SetEffect(cherrysoda::Graphics::GetEmbeddedEffect("sprite"));
renderer->KeepCameraCenterOrigin(false);
camera->UseOrthoProjection(true);
camera->CenterOrigin();
Add(renderer);

So my question is, how would you go on about rendering in layers, or is it something that needs to be implemented first?

(Also it seems that dynamically changing the Scale of a sprite is not supported. So can I assume that everything needs to be set up correctly on begin of a scene? And if so, how should zooming be accomplished?)

Thank you for your time!

Rendering primitives

I would like to ask if there is an intended way to render primitives like lines, circles etc. in 2D space. As my current code oriented on the example FirstTriangle seems not to be correct and not rendered...
Currently in my code I create a line with:

static void CreateLine2D(...):
              GetMesh()->Clear();

              GetMesh()->AddLine( MK_VERT(Vec3(from, 0.0f), color), MK_VERT(Vec3(to, 0.0f), color));

              GetMesh()->SubmitBuffer();

              // ...

             entity->Add(line);
             entity->AddTag(entityTag);
	     scene->Add(entity);

Where the line class inherits from MeshGraphicsComponent< PosColorVertex >.
The renderer is an EverythingRenderer with a "sprite"-Effect and OrthoProjection.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.