这篇博客,我们说一下Actor、Component和DrawComponent类的代码。代码都很简单,所以写到了一篇里。

Actor

这个类是角色类,保存了角色的一些基本信息。

代码

Actor.h:

#pragma once
#include <vector>
#include "Math.h"
#include<SDL.h>
#include"Game.h"
class Actor
{public:enum State{EActive,EPaused,EDead};Actor(class Game* game);virtual ~Actor();void ProcessInput(const uint8_t* keyState);virtual void ActorInput(const uint8_t* keyState);// Update function called from Game (not overridable)void Update(float deltaTime);// Updates all the components attached to the actor (not overridable)void UpdateComponents(float deltaTime);// Any actor-specific update code (overridable)virtual void UpdateActor(float deltaTime);// Getters/settersconst Vector2& GetPosition() const { return mPosition; }void SetPosition(const Vector2& pos) { mPosition = pos; }float GetScale() const { return mScale; }void SetScale(float scale) { mScale = scale; }float GetRotation() const { return mRotation; }void SetRotation(float rotation) { mRotation = rotation; }State GetState() const { return mState; }void SetState(State state) { mState = state; }class Game* GetGame() { return mGame; }// Add/remove componentsvoid AddComponent(class Component* component);void RemoveComponent(class Component* component);
private:// Actor's stateState mState;// TransformVector2 mPosition;float mScale;float mRotation;std::vector<class Component*> mComponents;class Game* mGame;
};

Actor.cpp:

#include "Actor.h"
#include "Component.h"
#include <algorithm>Actor::Actor(Game* game):mState(EActive), mPosition(Vector2::Zero), mScale(1.0f), mRotation(0.0f), mGame(game)
{mGame->AddActor(this);
}Actor::~Actor()
{mGame->RemoveActor(this);// Need to delete components// Because ~Component calls RemoveComponent, need a different style loopwhile (!mComponents.empty()){delete mComponents.back();}
}void Actor::ProcessInput(const uint8_t* keyState)
{if (mState == EActive){for (auto comp : mComponents){comp->ProcessInput(keyState);}ActorInput(keyState);}
}void Actor::ActorInput(const uint8_t* keyState)
{}void Actor::Update(float deltaTime)
{if (mState == EActive){UpdateComponents(deltaTime);UpdateActor(deltaTime);}
}void Actor::UpdateComponents(float deltaTime)
{for (auto comp : mComponents){comp->Update(deltaTime);}
}void Actor::UpdateActor(float deltaTime)
{}void Actor::AddComponent(Component* component)
{// Find the insertion point in the sorted vector// (The first element with a order higher than me)int myOrder = component->GetUpdateOrder();auto iter = mComponents.begin();for (;iter != mComponents.end();++iter){if (myOrder < (*iter)->GetUpdateOrder()){break;}}// Inserts element before position of iteratormComponents.insert(iter, component);
}void Actor::RemoveComponent(Component* component)
{auto iter = std::find(mComponents.begin(), mComponents.end(), component);if (iter != mComponents.end()){mComponents.erase(iter);}
}

代码分析

代码非常简单,我们现在来分析一下。

成员变量

mState:角色状态。
mPosition:角色坐标。
mScale:角色缩放比例。
mRotation:角色旋转角度。
mComponents:角色的组件。
mGame:与之关联的Game类。

构造函数

构造函数非常简单,就是初始化成员变量,然后调用Game类的AddActor函数。

析构函数

析构函数也很简单,就是调用Game类的RemoveActor函数,然后删除所有组件。

ProcessInput

这是角色的处理输入函数,但它并不是虚函数,不能被重写(虽然说语法上可以,但重写也没用,因为是通过基类指针调用的函数)。这只是个框架函数,遍历所有组件,执行ProcessInput,然后执行ActorInput函数。ActorInput才是可以被重写的虚函数。

ActorInput

用于处理角色的输入,可以被子类重写。代码实现为空。

Update

角色的更新。它调用UpdateComponents函数更新所有组件,然后调用虚函数UpdateActor。

UpdateComponents

遍历所有组件进行更新。

UpdateActor

用于更新角色,可以被子类重写。代码实现为空。

AddComponent

添加一个组件。这个函数和Game::AddDrawComponent函数实现基本相同,都是按照更新次序插入。

RemoveComponent

删除一个组件。这个函数和Game::RemoveDrawComponent函数实现基本相同,都是先find再用erase删除。

Getter/Setter函数

剩下的都是一些非常简单的获取信息和设置信息函数,这里不作讲解。

Component

这个类是组件类。

代码

Component.h:

#pragma once
#include<cstdint>
#include"Actor.h"
class Component
{public:// Constructor// (the lower the update order, the earlier the component updates)Component(class Actor* owner, int updateOrder = 100);// Destructorvirtual ~Component();virtual void ProcessInput(const uint8_t* keyState);virtual void Update(float deltaTime);int GetUpdateOrder() const { return mUpdateOrder; }
protected:// Owning actorclass Actor* mOwner;// Update order of componentint mUpdateOrder;
};

Component.cpp:

#include "Component.h"Component::Component(Actor* owner, int updateOrder):mOwner(owner),mUpdateOrder(updateOrder)
{// Add to actor's vector of componentsmOwner->AddComponent(this);
}Component::~Component()
{mOwner->RemoveComponent(this);
}void Component::ProcessInput(const uint8_t* keyState)
{}void Component::Update(float deltaTime)
{}

代码分析

这个类太简单了,其实也没啥好说的。

DrawComponent

绘画组件类。

代码

DrawComponent.h:

#pragma once
#include"Component.h"
class DrawComponent:public Component
{public:DrawComponent(class Actor* actor, int drawOrder = 100);~DrawComponent();virtual void Draw(SDL_Renderer* renderer);
};

DrawComponent.cpp:

#include "DrawComponent.h"
#include "Actor.h"
#include"Game.h"
DrawComponent::DrawComponent(Actor* actor, int drawOrder):Component(actor,drawOrder)
{mOwner->GetGame()->AddDrawComponent(this);
}
DrawComponent::~DrawComponent()
{mOwner->GetGame()->RemoveDrawComponent(this);
}
void DrawComponent::Draw(SDL_Renderer* renderer)
{}

代码分析

这个类更简单,没啥好说的。

RTTI(运行时类型识别)

为什么要介绍RTTI呢?因为这个功能也是在游戏中经常用到的。回忆一下,在Game类中,所有的角色都以Actor类的指针存储在一个容器里,无法区分角色是哪一种类型的。比如一个简单的飞机大战的游戏,会有炸弹角色和飞机角色,如果飞机角色碰到炸弹角色就会爆炸。但如果简单地遍历所有的Actor,判断是否碰撞,会发生这样一个现象:飞机会检测到自己和自己碰撞,或者和一些无关紧要的东西(比如己方的其他东西)碰撞,都会引发爆炸!这时候,我们就需要区分一下,只有飞机和炸弹碰撞,才会引发爆炸。可是怎么判断呢?这就需要运行时类型识别了。
RTTI的代码并不深奥,相反,只需要一行代码就可以。这里就用到了C++内置的一个关键字(或者说运算符):typeid。关于typeid,我们不需要深入了解,只要知道它是获取一个对象的类型信息就可以了。重要的地方是,即使是基类的指针指向子类对象,它也能正确判断出类型!所以,刚才那个飞机大战的代码可以这么写:

void 敌方炸弹::UpdateActor(float deltaTime)
{Vector2 pos = GetPosition();pos.y += deltaTime * mSpeed;if (pos.y > 768)SetState(EDead);SetPosition(pos);for (auto i : GetGame()->mActors){if (typeid(*i) == typeid(己方的子弹))//运行时类型检查,如果碰到子弹就消失{Vector2 bPos = i->GetPosition();if (碰到子弹){SetState(EDead);i->SetState(EDead);}}else if (typeid(*i) == typeid(飞机))//碰到飞机就结束游戏{Vector2 bPos = i->GetPosition();if (碰到飞机){SetState(EDead);i->SetState(EDead);SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "游戏结束", "游戏结束,你输了!", GetGame()->mWindow);GetGame()->mIsRunning = false;}}}
}

但是,如果只是这么写,是无法正常运行的,因为我们没有为项目开启运行时类型识别的功能。我们要找到项目-属性-C/C++所有选项,搜索“启用运行时类型信息”,选择“是(/GR)”,才能正常运行。

总结

到现在,我们的项目模板终于完成了!我们把它导出为模板,以后就可以使用这套框架代码了!如果没有错误,运行出来的结果应该这样的:

写了这么多代码,却只运行出这个,可能心里会有亿点点失望,不过我们的项目真正的意义是,我们创建出了一个非常完善的程序框架!在下一篇,我会带大家用这个框架代码做一个简单的小游戏,那时你就会发现,有了这个框架,扩展代码是多么简单。大家编程的时候,也要养成这种高度模块化的习惯,这样后期增加功能会非常方便哦!

C++游戏编程教程(四)相关推荐

  1. iPhone游戏编程教程一步步教你游戏开发

    这是此系列教程的第一部分,我将从最基本开始教大家怎样编写一个iPhone游戏.众所周知,OpenGL和Quartz的学习不是那么简单的.本教程将简化开发的过程而不会使用这两种技术.我们将使用UIIma ...

  2. Python游戏编程(四)Hangman

    Hangman是一个双人游戏,通常用纸和铅笔来玩.一个玩家想好一个单词,然在纸上为单词的每个字母画一个空格.然后第二个玩家猜测这个单词中可能包含的字母. 如果第二个玩家猜测对了,第一个玩家就在正确的空 ...

  3. 塔防游戏制作教程(四)

    嗨!大家好,我是小蚂蚁.今天我们继续分享制作一个塔防小游戏的第四节,如何实现炮塔的升级和出售功能. 如何实现炮塔的升级 在炮塔升级时,我们简单地做一些属性的提升以及外表的变化,例如当炮塔升级后,攻击速 ...

  4. C++游戏编程教程(六)——C++字符串消息处理器类

    大家好,我是一位初一的编程爱好者.今天,我向大家介绍一个我自制的C++字符串消息处理器类.看到标题,大家可能有些疑惑,字符串消息处理器怎么能和游戏编程扯上关系呢?其实,很多游戏中都需要用到消息处理,特 ...

  5. C++游戏编程教程(七)——改进飞机大战游戏

    注:在本篇博客中,对上一篇博客的飞机大战游戏进行了完善,但有很多细微的修改,由于篇幅原因,没有把所有代码列出来,大家需要仔细阅读,否则可能漏掉一些地方,导致编译错误或产生bug. PS:如果大家有什么 ...

  6. java四连环游戏编程_四连环游戏

    看到 UP@邻家小女爱数分(https://space.bilibili.com/485356815/dynamic)发的关于四连环流的题目,就想来写一下,看看能不能写出来. 发现还是挺有意思的.不过 ...

  7. C++游戏编程教程(二)

    上一篇博客,我们讲了SDL环境的配置和基本程序框架.这篇博客,我本打算讲一下Game类的代码,但突然想到很多API都没有介绍,所以这篇博客先来介绍一下SDL的基础知识和游戏运行框架,Game类的实现下 ...

  8. VB+DX游戏编程教程 第二话:点的艺术

    本博客所有内容均来源于  http://www.uoyo.net     . 高手还请多多包涵. 经过我们上一节的学习.我们已经可以初始化一个D3D设备了.好的,我们先来回顾一下这个步骤,毕竟这是我们 ...

  9. 麒麟子Javascript游戏编程零基础教程大纲

    大家好,我是麒麟子, 开源项目<幼麟棋牌-四川麻将>(泄漏版叫 <达达麻将>)作者,成都幼麟科技创始人. 开源项目地址(Github与Gitee同步更新): Github ht ...

最新文章

  1. 推荐65个以自然风光为背景的UI设计
  2. ASP.Net 使用css换肤(转)
  3. 如何使用小数据集对大模型进行微调(迁移学习)-微迁移
  4. qpython怎么用matplotlib_python-通过文本框的交互式matplotlib图
  5. 【转】Windows版本,OS内核版本,Windows SDK之间的关系
  6. 在Linux终端下调用可执行文件时总要加上符号./的原因
  7. 超级棒的免费前端学习路线
  8. 《流浪地球》内地票房超《红海行动》北美上映11天成绩不俗
  9. Android studio中提交svn一直卡在performing VCS commit
  10. 编程语言对比 导入模块
  11. 学习面向对象编程OOP 第一天
  12. python批量查看邮件_python win32com批量阅读outlook电子邮件打开的项目太多
  13. CDOJ 29 飞镖(dart) 解题报告
  14. 【二 HTTP编程】2. HTTP路由
  15. 内存颗粒的逻辑bank理解
  16. 计算机上网记录怎么清除,如何清除上网记录?
  17. 《人格心理学》读书笔记
  18. js 打印去掉页眉页脚页码_JS实现无页眉页脚打印(转)
  19. html 隐藏tbody,隐藏的tbody
  20. 离散数学笔记(一)【集合、命题逻辑 、谓词逻辑】

热门文章

  1. xnawindowsphCocos2d-x for XNA游戏引擎全面支持WP7
  2. php将上传的文件自动压缩,如何在服务器端实现文件自动压缩和解压
  3. 2021-05-19Leetcode238.除自身以外数组的乘积
  4. 请更换备份电池 pos机_给你一个更换POS机的理由!
  5. php牙医系统,牙医: 贝致口腔APP
  6. python怎么保留两位小数是多少_python 怎么保留2位小数
  7. 计算机在gis中的应用研究,空间聚类分析及其在GIS中的应用研究-计算机软件与理论专业毕业论文.docx...
  8. 新版芒果tv电脑版 v6.3.9官方版
  9. Python爬虫实战,requests模块,Python实现抓取芒果TV弹幕
  10. 主键冲突报什么代码_程序员经典面试题,Mysql自增主键为什么不连续