31
@ 原原原原 运运 Entity/Component 运运运运 运运运运运运运 HTML5 运运

运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Embed Size (px)

DESCRIPTION

运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏. @ 原木博皞. About Me. 唐博皞 Boyd Tang @原木博皞 Startuper/ 原木游戏工作室 Co-Founder 独立游 戏开发 者 折腾 Flash/HTML5 游 戏 高 端骨灰级游戏玩 家 ( 高玩 , 恩 = -= ). Outline. 游戏编程的演变 – 从数据驱动的 GameObject 结构再到 Entity/Component 架构 架构思路 - Entity/Component 的实现特点 - PowerPoint PPT Presentation

Citation preview

Page 1: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

@ 原木博皞

运用 Entity/Component 架构的游戏引擎快速开发 HTML5 游戏

Page 2: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

About Me

唐博皞 Boyd Tang@ 原木博皞 Startuper/ 原木游戏工作室 Co-Founder独立游戏开发者折腾 Flash/HTML5 游戏高端骨灰级游戏玩家 ( 高玩 , 恩 =-=)

Page 3: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Outline

游戏编程的演变 – 从数据驱动的 GameObject 结构再到 Entity/Component 架构

架构思路 - Entity/Component 的实现特点CraftyJS - HTML5 中的 Component Based 游

戏引擎Demo/Practice – HTML5 CodeJam 48 小时作

Page 4: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

如果要做一个游戏,你会怎么写?

有一个 Main 函数 function main() {

while(/* 游戏没结束 */){ /* 在循环里写游戏更新逻辑 */ }

/* 游戏结束出现游戏结果 */ }

更新逻辑是啥?一个 player 和一堆 monster player.update(); // player 需要不断更新

for( var i = 0; i< MAX_MONS; i++){monsters[i].update(); // 怪物也是

}那如果 Player 和 Monster 有通用的功能呢?

Page 5: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

GameObject – 经典的数据驱动结构

游戏世界中所有的物体都是GameObject 角色,怪物,环境障碍,车辆,

子弹,摄像头,触发器,灯光现在在 Main 函数中怎么写?

for( var i=0; i<numGameObjects; i++)

gameObjects[i].update();

GameObject 是什么呢?由数据驱动,而非代码写死

GameObject

Drawable

Static Simulated

Player Monster

Trigger

Page 6: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

我要这样? 还是这样?

GameObject

Drawable

Animated

Static Simulated

Player Monster

Trigger

如果想在 GameObject 结构下添加功能呢?

GameObject

Drawable

Static Simulated

Animated

Player Monster

Trigger

Page 7: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

更复杂的情况呢…

GameObject

Drawable

Static Simulated

Animated

Player Monster

Trigger

Physics ?

Page 8: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

经典 GameObject 结构的问题

很多功能无法单纯靠继承实现,最终的代码结构并不是一个有向无环图

类继承导致难以轻易改变结构功能全都向上依赖子类的数据爆炸,大量冗余数据和方法并导致内存消

耗过大

Page 9: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

那神马是 Entity/Component 架构?

Page 10: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

从 GameObject 到 GameEntity

游戏中所有的 Object 都是 GameEntity

GameEntity 只是一个容器GameEntity 里含有一堆独立功能的 Components

Component 之间的互相访问,通过 GameEntity

传递Component 可以实时增减,动态为 GameEntity

增减功能

Page 11: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

现在的 GameEntity 结构

Player

Drawable

Update

Animated

Position

Physics

Monster

Drawable

Update

Position

Physics

Props

Drawable

Animated

Position

Physics

Trigger

Update

Page 12: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Entity/Component 架构的实现特点

GameEntity 是一个容器,其主要功能仅仅是增减Components

组件 (Component) 才是功能的携带者,组件可以有一定依赖关系。 例如 : Animated 组件依赖于 Drawable 组件

属性 (Property) 是组件间互相访问的主要实现方法。属性由特定组件具有的一系列提供 setter 和 getter 方法。 例如 : Position 组件提供 x,y 属性, Update 组件进行修改。

Page 13: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

一些关于 Entity/Component 的常见问题

一个 GameEntity 可以有多个相同的 Component 么? 是的,理论上是可以的。一般 GameEntity 的实现可以根据

Component 的 ClassName 来进行 hash ,也可以通过给Component 实例设置 Name 进行 hash 。如果需要避免冲突,可以选择根据 ClassName 来实现。

传统的 GameObject 有个统一的 update , GameEntity的 update 呢,是一个 Component 嘛? 其实并不是这样的, GameEntity 的 update 可以是 interface ,当

Component 实现此 interface 后,即具备了 update 功能。也可以是事件驱动下, Component 监听发到 GameEntity 的 update 事件。

整个游戏世界中 GameEntity 是全都是同级的么? GameEntity 的实现时,可以增加 GameEntityGroup 的实现 () 。

这样就可以对其进行分组管理。

Page 14: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

使用 Entity/Component 架构的游戏引擎

大型 3D 游戏引擎 – Unity

大家应该都知道这名字吧=-=

Flash 游戏引擎 – PushButton

推荐 PBE2 ,比 PBE1 轻量很多

HTML5 游戏引擎 – CraftyJS

本期的重点介绍

Page 15: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty – 用 JS将灵活发挥到极致

轻量的体积: 14.5KB (Minified&Gzipped)

类似 JQuery 的选择器用于 GameEntity 的选择同时支持 Canvas或者 DOM 进行渲染

( 不用 Canvas渲染连 IE6 都能跑 !)

事件驱动,有非常好用的 Event 系统支持 SpriteSheet ,碰撞检测,声音等

Page 16: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 的选择器

通过查询 Component 来进行选择 Crafty(“mycomp”); Crafty(“hello 2D mycomp”); Crafty(“hello, 2D, mycomp”);

第一个返回全部具有 mycomp 组件的 GameEntity第二个返回全部同时具有 hello, 2D 和 mycomp 组

件的 GameEntity ( AND操作)第三个返回至少有这些组件之一的全部

GameEntity(OR操作)

Page 17: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 的基本使用

创建GameEntity var player = Crafty.e();

为 GameEntity 添加 Component player.addComponent(“2D, DOM”) // 添加 2D 组件,使用

DOM渲染 也可以在创建GameEntity 时添加 Crafty.e(“2D, DOM”);

为 GameEntity 设置属性 player.attr({ x:5, y:5, w:100, h:100}); // 当具有 2D 组件时,

GameEntity 即拥有了 x,y,w,h等属性级联操作

player.addComponent(“2D, color”).color(“red”).attr({ w:100, h:100});

Page 18: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

自定义你的 Compoent

使用 Crafty.c() 方法进行自定义 Crafty.c(“mycomp”, { // 为自定义组件命名

init: function() { //init 函数在被组建被添加时调用 // 自定义组件函数中的 this ,均指向所属的 Entity this.requires(“2D, Color”); //requires 方法可指定依赖

组件 this.w = 32; this.h = 32; this.color(“red”); // 也可以这样写 : this.attr({w:32, h:32 }).color(“red”);},myfunc: function() { /*我的方法实现 */ }

})

Page 19: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 的事件系统

通常事件会在自定义组件的 init 中绑定 Init: function() {

this.requires(“2D, DOM, Mouse”);// 可以使用 bind 方法绑定事件this.bind(“Enterframe”, function( e) { //Enterframe

可任意监听 … // 即 update 函数});this.bind(“Click”, function(e){ // 当添加 Mouse 组件后可用 … // 当鼠标点击Entity 时的处理函数})

}

事件触发 : this.trigger(“event” , data);

Page 20: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

属性与 Setter 方法、 Change 事件

属性获取、更改 (若无 setter, 首次使用即定义 ) attr(name, value)或者 attr({name:value})均更改属性 attr(name) 可获取属性

针对特定属性的处理: setter Crafty.c(“mycomp”, {

_hp: 100,init: function() { this.setter(“hp”, function(v){ this._hp = v; /* 一些

判断 */});}

});针对全部属性改变的事件:Change

this.bind(“Change”, function(){ /* 一些判断 */ })

Page 21: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 中的关卡场景定义

关卡场景定义 Crafty.scene(“sceneName”, function(){

…//执行一些函数,创建一堆 Entity ,设置背景, bhla bhla…});

关卡场景调用 Crafty.scene(“sceneName”); // =_____= 没啥好说的…

关卡场景切换时发生了啥? 开始某关卡时, Stage 当前所有具有 2D 组件的 Entity 都会被销毁

若希望保留某些 Entity ,给它添加 Persist 组件即可 关卡切换时 SceneChange 事件将触发

Page 22: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

引擎的启动与停止

Crafty.init([width, height]); 长宽默认是全屏 引擎 init 后,将持续触发 Enterframe 事件 , 即为游戏提供

update 。 引擎启动瞬间,将触发 Load 事件

Crafty.stop(); 当引擎停止后将移除 Enterframe 事件的 setInterval 同时也将移除全部在 Stage 中的元素

Page 23: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

使用 SpriteSheet

使用 Crafty.sprite 函数创建 Sprite 组件 Crafty.sprite(16, 16, “spirte.png”, { // 定义每格长宽

grass1:[0,0], // 将此 SpriteSheet 的 0,0格位置定义为grass1

grass2:[1,0], // 同上定义 1, 0格为 grass2grass3:[2,0],grass4:[3,0],flower:[0,1], // flower 定义在 0,1格bush:[0,2],player:[0,3], // 玩家定义在 0, 3格位置

});定义 Sprite 组件后,创建GameEntity

Crafty.e(“2D, DOM, flower”);// 此时组件已定义,将渲染 0,1 格的 flower

Page 24: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

使用 Sprite 动画

添加 SpriteAnimation 组件即可使用 Sprite 动画 var player = Crafty.e(“2D, DOM, player,

SpriteAnimation”).attr({ x:100, y: 100}).animate(“left”, 6, 3, 8) // 定义 left 为 x:6, y:3格

开始到 x:8格.animate(“right”, 9, 3, 11)// 同上.animate(“up”, 3, 3, 5).animate(“down”, 0, 3, 2);

播放动画 player.animate(“left”, 10); // 在 10帧时间内播放 left 动画

其他函数 stop(), reset(), isPlaying(name)

Page 25: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 中的碰撞检测

基于 Crafty.Polygon或者 Crafty.Circle 的碰撞检测添加 Collision 组件后即可使用

var player = Crafty.e(“2D, DOM, player, Collision”).collision() //默认创建基于 x,y,w,h 的 Polygon 用于检

测.onHit(“flower”, function(){ …// 进行一些处理});

onHit 方法是自动对具有某个 Component 进行检测 当与具有该组件的 GameEntity碰撞时即调用注册的函数

也可以直接调用 hit(compName)检测 此方法返回 Boolean

Page 26: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 中的键盘操控

基本组件: Keyboard 组件 函数: isDown(key) // 可以在 Crafty.keys 中找到映射 事件触发: KeyDown , KeyUp

高级组件:Multiway 组件(依赖 Keyboard) 函数:multiway([speed], {W: -90, S:90, D:0, A:180} ); 事件触发:NewDirection( 方向切换时 ), Moved( 发生移动

时 )

便捷组件: Fourway, Twoway ( 依赖 Multiway) fourway // wasd四方向控制 twoway // ad 双方向, w跳可在 Gravity 组件中使用

Page 27: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafty 中的鼠标操控

基本组件 : Mouse 组件 函数: areaMap(polygon) // 传入一个 Crafy.Polygon 作为检测区

事件触发 : MouseOver, MouseOut, MouseUp, MouseDown, Click

高级组件:Draggable 组件(依赖 Mouse 组件) 函数 : startDrag, stopDrag, enableDrag, disableDrag 事件触发: StartDrag, StopDrag

对于移动平台的 Touch ,同样使用 Mouse 组件 暂无对 MultiTouch 的支持

Page 28: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

资源预加载与音频播放

资源预加载与音频分别需要 Image 和 Audio 的支持使用 Crafty.load 进行资源预加载

Crafy.load([/*资源 url 列表 */], onComplete, onProgress, onError);

预加载可以先定义一个 loading 的 scene ,进入并进行Crafy.load 。在 onComplete 函数中切换到 main scene

音频的命名和播放 命名: Crafty.audio.add(id, urls); //urls 传入多种格式,播放时引擎会选择其中一个兼容的格式

播放: Crafty.audio.play(id);

Page 29: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

Crafy与其他 JS框架相结合进行游戏开发

Crafty 在创建 Stage 时会查找 id 为 cr-stage 的div

Crafty 引擎擅长处理具有游戏性 GameEntity

UI 并不是 Crafy擅长的,可以选择其他框架 当然使用 DOM 组件处理 UI 也是可以的。

为其他 JS框架定义 Component 以传递数据对中文的处理最好使用 DOM

Page 30: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

一些 Crafty 的 Demo 和 Practice

官方的 Demo RPG Isometric Asteroids Connect4 FruitAssassion

HTML5 CodeJam 上的 Practice 杀戮者

Page 31: 运用 Entity/Component 架构的游戏引 擎 快速 开 发 HTML5 游戏

屏幕底下的一行小字 = - =

谢谢大家听我扯了这么久!

Q&A