OpenGL Game Engine (Lance2D)

Introduction

인트로

介紹

오픈지엘을 활용한 2D 게임 개발을 위한 간단한 게임 엔진.

Simple game engine for 2D game development using OpenGL.

利用OpenGL游戏开发2D游戏引擎。

상업목적이 아닌 개인 공부용으로 개발하였다.

Developed for personal study, not for commercial purposes.

开发为个人学习而不是为了商业目的。

간단한 맵 에디터를 제공하고 게임오브젝트에 실시간으로 애니메이션을 적용시킬 수 있다. 또한 쉐이더 컴파일 기능을 제공하여 맵툴에서 실시간으로 쉐이더를 제작할 수 있다.

Provides a simple map editor and enables real-time animations to be applied to game objects. Shaders can also be created in real time in the map tool by providing shader compilation.

可以提供简单的编辑,在游戏中实时应用动画。 然后还提供材质编译功能,实时在引擎上可以制作。

게임오브젝트는 기본적으로 2D 플랜과 3D 오브젝트(OBJ) 로더를 제공한다. 또한 테트리스 게임개발에 특화하여 테트리스 블럭 오브젝트를 따로 정의하였다.

Game object provides 2D plane and 3D object (OBJ) loader basically. In addition, the Tetris Block object was defined separately by specializing in the development of Tetris game.

Game Object提供基本的2D平面和3D模型导入(OBJ)。另外,为了专门开发Tetris游戏定义了Tetris积木模型。

자신만의 게임오브젝트를 원한다면 Object를 상속받아 새로운 형태의 게임오브젝트를 제작 가능하다.

If you want your own game object, you can create a new type of game object by inheriting it.

如果想要自己的Game Object,可以继承Object,制作新的Game Object。

또한 UI 제작을 위한 위젯을 제공한다. 현재는 버튼과 라벨만을 제공하며 버튼은 게임씬 코드에서 원하는 콜백메소드를 바로 바인딩하여 사용할 수 있다.

It also provides widgets for UI creation. Currently, only buttons and labels are provided, and buttons can be used by binding the callback method directly into the game scene code.

并且为UI制作提供了Widget。 现在只提供按钮和标签,按钮直接可以链上在GameScene代码想要的Callback函数。

사용자 정의 컴포넌트를 제공하며 게임오브젝트에 추가하여 런타임시 독립적인 로직을 구성할 수 있다.

It provides user-defined components and can be added to a game object to configure independent logic at run-time.

提供User-Define Component,添加到GameObject上,在游戏运行时可以构成独立的logic。

씬그래프에서 게임 오브젝트간의 계층 구조를 손쉽게 적용할 수 있다. 기본적인 3D 계층구조를 적용하여 하위 노드는 상위 노드의 트렌스폼 속성을 상속받는다.

It is easy to apply the hierarchical structure between game objects in the scene graph. By applying the basic 3D hierarchy, the child node inherits the transform properties of the parent node.

在Scene Graph中,每个游戏模型的层次结构可以容易适用。通过应用基本3D层次结构,子节点继承了母节的变换性质。

제작된 씬은 파일로 저장하여 런타임시 파일 매니져를 통해 불러와서 사용할 수 있다. 또한 각각의 씬을 씬메니져에 등록하면 손쉽게 서로 다른 씬을 전환할 수 있다.

The created scene can be saved as a file and imported through the File Manager at run-time. In addition, registering each scene in the scene manager makes it easy to switch between different scenes.

制作好的GameScne可以存为文件,使用时通过文件管理员传召。另外,在每个GameScene登记到Scene Manager的话,可以轻松其换不同的GameScene。

Github Source Code

Environment

개발환경

开发环境

OpenGL

Qt5

C++

GLSL

Development Time 

개발 기간

开发时间

3개월

3 month

三个月

Number of people

개발인원

开发人员

1인 개발

My self

个人发展

리뷰

Review

细节

게임 라이프 사이클

Game Life Cycle

Game Life Cycle

Game Life Cycle

맵툴

Map Tool

Map Tool

Tool Main Window

게임 씬

Game Scene

Game Scene

하나의 게임이 동작하려면 게임씬을 상속받아 최소 한개의 새로운 게임씬을 정의해야한다. 엔진에서 제공하는 게임씬을 상속받으면 게임로직, 렌더링, 인풋 이벤트 등의 메소드들을 사용할 수 있다.

For one game to work, you must inherit the game scene and define at least one new game scene. If you inherit the game scenes provided by the engine, you can use methods such as game logic, rendering, and input event.

一个游戏要想运行,必须继承GameScene,定义最少一个新的游戏场景。继承引擎提供的GameScene后,可以使用游戏logic,Render,Input Event等函数。

이렇게 정의된 새로운 씬은 게임 실행 이전에 씬메니져에 등록해야 한다.

The defined scene must be register to scene manager before the play for game.

这样定义的新GameScene,在游戏运行之前,必须注册在Scene Manager。

씬메니져에 등록된 이후부터 씬 인스턴스는 씬 메니져에 의해 관리된다.

After the register in scene manager, the instance is managed by the scene manager.

从在SceneManager注册后,Scene Instance就被SceneManager管理。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

// MyGameScene.h

#include <Scene/GameScene.h>

class MyGameScene : public GameScene
{
public:
    void InitializeGame() override;
	void GameLogic(double &time) override;
	void Render(glm::mat4 &VP, double &time) override;
};
// MyGameScene.cpp

MyGameScene *scene1 = new MyGameScene();
SceneManager::AddGameScene(*scene1);

이벤트

Event

Event

기본적으로 게임 씬에는 키보트와 마우스 이벤트를 제공한다.

By default, game scenes provide keyboard and mouse events.

基本上GameScene都会提供键盘和鼠标的Event。

아래의 함수들을 재정의하면 해당 이벤트들을 사용할 수 있다.

These events can be used by overriding the functions below.

对以下函数进行重新定义后,可使用相应Event。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

// MyGameScene.h

void OnKeyPressed(int Event) override;
void OnKeyReleased(int Event) override;
void OnMousePressed(int Event, double x, double y) override;
void OnMouseMove(double x, double y) override;
void OnMouseReleased(int Event, double x, double y) override;
// MyGameScene.cpp

void MyGameScene::OnKeyPressed(int Event)
{
    switch (Event)
    {
    case KEY_UP:			
        /*
        Do Something...
        */
        break;
    default:
        break;
    }
}

애니메이션

Animation

动画

애니메이션은 두가지 형태로 제공된다. 첫번째는 스태틱 오브젝트에 적용된 애니메이션이 전체 프레임 안에서 반복적으로 실행된다.

Animation is provide in two types. The first, the animation apply to the static object is played repeat within the entire frame.

动画提供两种模式。第一,应用于Static Object的动画在整个帧数内重复播放。

이 애니메이션은 컷씬이나 오브젝트에 반복적으로 애니메이션을 적용할 때 사용하면 유용하다.

This useful for sequence shot or repeat animation.

这对于场景动画或重复动画是有用的。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

Set Animation Keys

두번째는 런타임시 특정 이벤트에 작동하도록 하는 애니메이션이다. 이것은 씬의 타임라인에 의존하지 않고 독립적인 실행시간을 갖는다. 때문에 런타임시 원하는 동작에 대하여 애니메이션을 발생시킬 수 있다.

The second is an animation that lets you run on a specific event at run-time. This does not rely on the timeline of the scene, but has independent execution time. Therefore, it is possible to create an animation about the desired motion during run-time.

第二个动画是,在游戏时,让特定Event的启动。这并不依赖于引擎的时间,而是独立播放时间。所以,在游戏运行时,可以在特定时间实施动画片。

예를들어 게임 오브젝트가 사라지거나 이동하는 동작에서 애니메이션을 적용하면 부드럽게 사라지거나 선형적으로 이동하는 움직임을 만들 수 있다.

For example, applying animation to the motion of a game object disappearing or moving can create a movement that disappears smoothly or moves linearly.

比如,模型消失或移动的动作中,如果使用动画技术,可以实现柔和消失或线性移动。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

// MyGameScene.cpp

// Create the Anomator Instance that animation for some object
Animator* animator = new Animator();

// Assign to any keys what you want to mation
// This means to Start time of 0, at the 0 position
animator->animation.SetTranslateKey(0.0f, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));   
// Move to 10 unit of y position during in 10 frames. 
animator->animation.SetTranslateKey(10.0f, glm::vec4(0.0f, 10.0f, 0.0f, 1.0f)); 

// Set target object that you want apply to animation.
animator->SetTargetObject(object);

// Add animator in animation manager
SceneManager::animationManager.AddAnimator(*animator);


// OtherScene.cpp
// And finally you can play your animation in any other scope.
SceneManager::animationManager.PlayAnimators();

씬 그래프

Scene Graph

Scene Graph

게임에 등장시키고 싶은 모든 오브젝트(캐릭터, 적, 장애물 등등)는 생성과 동시에 씬그래프에 추가해야한다.

All objects (characters, enemies, obstacles, etc.) that want to appear in the game must be add in the scene graph at the same time as they are created.

游戏中想要用的所所有的东西(角色,敌人,等)在生成的同时必须添加在SceneGraph上。

이렇게 추가된 오브젝트들은 씬그래프에 의해 관리 된다. 객체의 메모리와 렌더링은 모두 씬그래프가 관리하므로 게임씬에서 따로 추가 코드를 작성할 필요가 없다.

These added objects are managed by a scene graph. Since both the memory and rendering of an object are managed by a scene graph, there is no need to additional code in the game scene.

添加后,这些东西就由SceneGraph管理。GameObject的内存和渲染都由SceneGraph管理,因此没必要在游戏中另外追加代码。

또한 직접 생성자를 호출할 필요가 없고 엔진의 씬 관리자를 통해 호출하면된다.

You also do not need to invoke the constructor directly, but you can call it through the scene manager.

然后不必直接调用生成子函数,但可以通过Scene Manager来调用。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

// MyGameScene.cpp

TetrisBlock &tBlock = (TetrisBlock&)SceneManager::CreateObject(OBJECTTYPE::block0);
SceneRoot->RootNode->AddChild(tBlock);

만일 이미 생성된 오브젝트에 접근하고 싶다면 씬그래프에서 해당 오브젝트를 얻어올 수 있다.

If you want to access some object that stored in scene(Level) files, you can get from scene graph.

如果想要获取已经生成的模型,可以在SceneGraph上获得相应节点。

Object &obejct = SceneRoot->FindNode("Object Name").GetNodeObject();

씬 그래프 – 어드벤스드

Scene Graph – Advanced

Scene Graph – Advanced

하나의 씬은 비순환 그래프(DAG : Tree)의 노드로 구성된다. 씬에는 하나의 최상위 루트 노드만 존재한다. 하나의 노드는 인접노드 리스트를 포함하며 부모 노드의 포인터를 함께 가지고 있다. 그리고 노드의 실제 데이터인 게임 오브젝트 포인터를 가지고 있다.

Acyclic graph is a scene (DAG : Tree ) of nodes. There is only one top-level root node in the scene. One node contains a list of adjacent nodes and has a parent node pointer together. And it has a game object pointer, the actual data of the node.

一个场景由Acyclic graph(DAG: Tree)的节点组成。场景里只有一个最高级的根节点。一个节点包括相邻节点列表,同时包含父母节点的Pointer。还有包含节点的实际数据——Game Object Pointer

// <Core/Node.h>

class Node
{
private:
	Object* object;
	Node* ParentNode;
	std::vector<Node*> ChildNodes;
}

게임 오브젝트는 자신이 어떤 노드에 위치해 있는지 알지 못한다. 때문에 게임오브젝트를 순회하려면 루트노드에서 해당 게임 오브젝트로 찾아 들어가야한다.

The game object doesn’t know which node it is located on. Therefore, if you want to For-loop the game objects, you have to start from the root node visit each child nodes.

Game Object不知道自己放在什么样的节点。所以想要循环Game Object的话从根节点搜索相关的Game Object。

순회 방식은 DFS와 BFS를 지원하며, 순회중 해당 요소에 특정 행동을 수행하려면 직접 정의한 함수포인터를 넘겨주는 방식으로 작업한다.

The For-loop method supports DFS, BFS. To perform a specific action on a given element during a For-loop, work by handing over the function pointer you have defined.

循环方式提供BFS和DFS, 如果循环中想要对相关的东西进行什么特殊的算法,就以直接给定义的函数Pointer的方式进行工作。

각각의 순회 함수는 리턴값을 포함한 것과 그렇지 않은것 두가지 형태를 지원한다.

Each For-loop method supports two types, one involving a return value and the other not.

每个循环函数支持包括返回值和没包括的两种形式。

아래는 선택된 오브젝트를 얻어오는 예시이다.

Below is an example of the getting a selected object.

下面的例示得到选择的GameObject。

// <Core/Node.cpp>
// Define the method according to the predefined function pointer of prototype
void* SelectedObject(Node& node, const void* source)
{
	Node* resNode = nullptr;
	if (node.GetNodeObject().IsSelected)
	{
		resNode = &node;
	}
	return resNode;
}


// <Controller/SceneGraph.cpp>
// How to use
// Define the return variable
void* returnValue = nullptr;
// Call function that execute method in root node,
// and send arguments the your function with variables.
RootNode->ExecuteReturn_B(&SelectedObject, returnValue);
// Casting the return variable to node
Node* node = (Node*)returnValue;
// If node pointer is not null, get object in the node instance.
Object* object = nullptr;
if (node)
{
    object = &node->GetNodeObject();
}

게임 컴포넌트

Game Component

Game Component

게임오브젝트에 새로운 기능을 추가하거나 게임의 로직을 분리하는 등, 특정 게임오브젝트의 행동을 제어하고 싶다면 게임 컴포넌트를 사용하면 아주 유용하다.

The game components is very useful if you want to control the behavior of a particular game object, such as adding new features to a game object or separating the logic of the game.

对Game Object添加新功能或分开游戏logic等,如果想控制特定的Game Object的行动,使用Game Component非常有用。

게임 컴포넌트는 모든 게임 오브젝트에 추가하여 사용할 수 있다.

Game components can be used in add to all game objects.

GameComponent可添加到所有GameObject中使用。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

// ObjectBehaviour.h

#include <GameComponent/GameComponent.h>

// Defines the sub class of GameComponent
class ObjectBehaviour : public GameComponent
{
public:
	ObjectBehaviour();
	~ObjectBehaviour();
	virtual void Update(double &Time) override;

	Object* GameObject;
};
// MyGameScene.cpp

// Instanciate your GameComponent
ObjectBehaviour *objectBehaviour = new ObjectBehaviour();
// Set a specific name.
// Later, the component can be obtained through this name.
objectBehaviour->Name = "ObjectBehaviour";

// Set up a parent object to include that component.
// You don't need to set this variable if you don't need it for your logic.
objectBehaviour->GameObject = MyGameObject;

// Add components to the corresponding game object.
MyGameObject->AddComponent(objectBehaviour);

UI

게임에 사용되는 UI 버튼 컴포넌트는 생성후 원하는 콜백함수를 바인딩해주어야 한다.

The UI button component used in the game must bind with callback function after creation.

游戏中使用的UI按钮生成后,必须要绑定Callback函数。

씬에서 직접 객체 생성하는 예시.

Example of creating an object directly from a scene.

在场景里直接生成客体的例子。

// MyGameScene.cpp

glButton &MyButton = (glButton&)SceneManager::CreateObject(OBJECTTYPE::BUTTON);
SceneRoot->RootNode->AddChild(MyButton);
MyButton.Connect(this, &MyGameScene::MyCallbackFunction);

씬그래프에서 해당 엘레멘트를 찾아서 바인딩 하는 예제.

An example of finding and binding a corresponding element in a scene graph.

在Scene Graph上找到相应东西进行绑定的例题。

glButton &MyButton = (glButton&)SceneRoot->FindNode("My Button 1").GetNodeObject();
MyButton.Connect(this, &MyGameScene::DoSomething);

쉐이더

Shader

Shader

버텍스 / 프래그먼트 쉐이더 파일을 실시간으로 컴파일하여 뷰포트에서 확인 가능하다.

It is possible to compile a Vertex / Fragment Shader file in real time and check it in the viewport.

Vertex/Fragment Shader 文件可以实时编译,在引擎上确认。

Editing Shader

게임 데이터

Game Data

游戏数据

게임의 스코어를 관리할 수 있는 컨테이너다. 파일로 저장하기 때문에 게임 종료 이후에도 동일한 정보를 유지시켜준다.

It’s a container that can manage the score of the game. It keeps the same information even after the game ends because it is saved as a file.

这是可以管理游戏分数的集装箱。

또한 내부적으로 우선순위큐를 이용하기때문에 최고/ 최소 점수를 쉽게 얻어올 수 있다.

In addition, the highest/lowest score can be easily getting by using priority-queue internally.

而且由于内部使用Priority-queue,所以很容易获得最高/最低分数。

아래는 간단한 예시이다.

Below is a simple example.

下面是一个简单的例子。

// MyGameScene.cpp

// The user's score and level are updated during the runtime game.
score++;
GameDataManager::SetUserScore(score);

level++;
GameDataManager::SetUserLevel(level);

// Save the current game data at the game over.
GameDataManager::SaveGameData();


// Data can be get from game data stored in other scenes, 
// ranking 1, 2 and 3.
1st->SetText(GameDataManager::GetTopScore());
2nd->SetText(GameDataManager::GetTopScore());
3rd->SetText(GameDataManager::GetTopScore());

테트리스 (샘플 게임)

Tetris (Sample Game)

Tetris (Sample Game)

이 테트리스 게임은 해당 엔진을 활용하여 제작한 기본 예시 게임이다.

这游戏是使用这个引擎制作的基本例子游戏。

The Tetris game is a basic example game made using this engine.

주요 로직으로는 오브젝트 풀링 기법을 사용하여 블럭의 생성과 삭제를 효율적으로 관리하고 있다.

As a major logic, it use object pooling techniques to efficiently manage the creation and deletion of blocks.

主要logic采用Object Pooling技术,有效管理模块生成和删除。

블럭 한행을 클리어할 때 런타임 애니메이션을 적용하여 역동적인 움직임을 연출하였다.

When cleaning the block rows, the animation was applied to create a dynamic movement.

在删除积木一排的时候,采用了run-time动画,应用动态运动的。

또한 게임에 사용된 많은 그래픽 리소스는 텍스쳐를 사용하지 않고 실시간으로 쉐이더에서 처리하고 있기때문에 텍스쳐에 사용되는 메모리를 줄일 수 있다.

Also, many of the graphic resources used in games are handled by shaders in real time without using textures, which can reduce the memory used for textures.

另外,游戏中使用的很多贴图不使用质地,而是实时在shader上处理所以可以减少贴图中使用的内存。

Game Play

Leave a Reply

Your email address will not be published. Required fields are marked *