CocosCreator + JavaScript游戏开发
搓搓小手,开始激动的开发小游戏。
会js就行,别的随缘学习。
JS入门就跳过了。
一. CocosCreator 入门
时长占比最大的手机游戏端引擎。
Cocos引擎_游戏开发引擎
文档 Introduction · Cocos Creator
26.节点的使用_哔哩哔哩_bilibili
1. 生命周期
- onLoad 加载成功时
- onEnable 当组件的
enabled
属性从false
变为true
时,或者所在节点的active
属性从false
变为true
时,会激活onEnable
回调。倘若节点第一次被创建且enabled
为true
,则会在onLoad
之后,start
之前被调用。 - start
start
回调函数会在组件第一次激活前,也就是第一次执行update
之前触发。start
通常用于初始化一些中间状态的数据,这些数据可能在 update 时会发生改变,并且被频繁的 enable 和 disable。 - update 游戏开发的一个关键点是在每一帧渲染前更新物体的行为,状态和方位。这些更新操作通常都放在
update
回调中。 - lateUpdate
update
会在所有动画更新前执行,但如果我们要在动效(如动画、粒子、物理等)更新之后才进行一些额外操作,或者希望在所有组件的update
都执行完之后才进行其它操作,那就需要用到lateUpdate
回调。 - onDisable 当组件的
enabled
属性从true
变为false
时,或者所在节点的active
属性从true
变为false
时,会激活onDisable
回调。 - onDestroy 当组件或者所在节点调用了
destroy()
,则会调用onDestroy
回调,并在当帧结束时统一回收组件。
2. 节点
节点有子父关系,子对于父相当于相对定位。子坐标的计算全都基于父节点的本地坐标系。
(1)最简单的就是空节点,可以作为容器承接其他节点,进行统一管理。
(2)3D对象
(3)UI节点
ui节点会自动生成根节点canvas,而且canvas会自动在屏幕中间展示。并且有适配方案。
(4)2D节点
(5)除了常见的渲染节点外,很多时候也会创建逻辑节点用来挂载脚本。
3. quick Start
脚本
GameManger.Ts
import { _decorator, Component, Prefab, instantiate, Node, Label, CCInteger, Vec3 } from 'cc';
import { PlayerController } from "./PlayerController";
const { ccclass, property } = _decorator;// 赛道格子类型,坑(BT_NONE)或者实路(BT_STONE)
enum BlockType {BT_NONE,BT_STONE,
};enum GameState {GS_INIT,GS_PLAYING,GS_END,
};@ccclass("GameManager")
export class GameManager extends Component {// 赛道预制@property({ type: Prefab })public cubePrfb: Prefab | null = null;// 赛道长度@property({ type: CCInteger })public roadLength: Number = 50;private _road: BlockType[] = [];// 主界面根节点@property({ type: Node })public startMenu: Node | null = null;// 关联 Player 节点身上 PlayerController 组件@property({ type: PlayerController })public playerCtrl: PlayerController | null = null;// 关联步长文本组件@property({ type: Label })public stepsLabel: Label | null = null!;start() {this.curState = GameState.GS_INIT;this.playerCtrl?.node.on('JumpEnd', this.onPlayerJumpEnd, this);}init() {// 激活主界面if (this.startMenu) {this.startMenu.active = true;}// 生成赛道this.generateRoad();if (this.playerCtrl) {// 禁止接收用户操作人物移动指令this.playerCtrl.setInputActive(false);// 重置人物位置this.playerCtrl.node.setPosition(Vec3.ZERO);// 重置已经移动的步长数据this.playerCtrl.reset();}}set curState(value: GameState) {switch (value) {case GameState.GS_INIT:this.init();break;case GameState.GS_PLAYING:if (this.startMenu) {this.startMenu.active = false;}if (this.stepsLabel) {this.stepsLabel.string = '0'; // 将步数重置为0}// 会出现的现象就是,游戏开始的瞬间人物已经开始移动// 因此,这里需要做延迟处理setTimeout(() => {if (this.playerCtrl) {this.playerCtrl.setInputActive(true);}}, 0.1);break;case GameState.GS_END:break;}}generateRoad() {// 防止游戏重新开始时,赛道还是旧的赛道// 因此,需要移除旧赛道,清除旧赛道数据this.node.removeAllChildren();this._road = [];// 确保游戏运行时,人物一定站在实路上this._road.push(BlockType.BT_STONE);// 确定好每一格赛道类型for (let i = 1; i < this.roadLength; i++) {// 如果上一格赛道是坑,那么这一格一定不能为坑if (this._road[i - 1] === BlockType.BT_NONE) {this._road.push(BlockType.BT_STONE);} else {this._road.push(Math.floor(Math.random() * 2));}}// 根据赛道类型生成赛道let linkedBlocks = 0;for (let j = 0; j < this._road.length; j++) {if (this._road[j]) {++linkedBlocks;}if (this._road[j] == 0) {if (linkedBlocks > 0) {this.spawnBlockByCount(j - 1, linkedBlocks);linkedBlocks = 0;}}if (this._road.length == j + 1) {if (linkedBlocks > 0) {this.spawnBlockByCount(j, linkedBlocks);linkedBlocks = 0;}}}}spawnBlockByCount(lastPos: number, count: number) {let block: Node | null = this.spawnBlockByType(BlockType.BT_STONE);if (block) {this.node.addChild(block);block?.setScale(count, 1, 1);block?.setPosition(lastPos - (count - 1) * 0.5, -1.5, 0);}}spawnBlockByType(type: BlockType) {if (!this.cubePrfb) {return null;}let block: Node | null = null;switch (type) {case BlockType.BT_STONE:block = instantiate(this.cubePrfb);break;}return block;}onStartButtonClicked() {// 点击主界面 play 按钮,开始游戏this.curState = GameState.GS_PLAYING;}checkResult(moveIndex: number) {if (moveIndex < this.roadLength) {console.log('跳到了坑上');if (this._road[moveIndex] == BlockType.BT_NONE) {this.curState = GameState.GS_INIT;}} else {console.log('跳过了最大长度');this.curState = GameState.GS_INIT;}}onPlayerJumpEnd(moveIndex: number) {if (this.stepsLabel) {// 因为在最后一步可能出现步伐大的跳跃,但是此时无论跳跃是步伐大还是步伐小都不应该多增加分数this.stepsLabel.string = '' + (moveIndex >= this.roadLength ? this.roadLength : moveIndex);}// 检查当前下落道路的类型,获取结果this.checkResult(moveIndex);}// update (deltaTime: number) {// // Your update function goes here.// }
}
PlayerController.Ts
import { _decorator, Component, Vec3, input, Input, EventMouse, Animation, SkeletalAnimation } from 'cc';
const { ccclass, property } = _decorator;@ccclass("PlayerController")
export class PlayerController extends Component {@property({type: Animation})public BodyAnim: Animation|null = null;@property({type: SkeletalAnimation})public CocosAnim: SkeletalAnimation|null = null;// for fake tweenprivate _startJump: boolean = false;private _jumpStep: number = 0;private _curJumpTime: number = 0;private _jumpTime: number = 0.3;private _curJumpSpeed: number = 0;private _curPos: Vec3 = new Vec3();private _deltaPos: Vec3 = new Vec3(0, 0, 0);private _targetPos: Vec3 = new Vec3();private _curMoveIndex = 0;start () {}reset() {this._curMoveIndex = 0;}setInputActive(active: boolean) {if (active) {input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);} else {input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this);}}onMouseUp(event: EventMouse) {if (event.getButton() === 0) {this.jumpByStep(1);} else if (event.getButton() === 2) {this.jumpByStep(2);}}jumpByStep(step: number) {if (this._startJump) {return;}this._startJump = true;this._jumpStep = step;this._curJumpTime = 0;this._curJumpSpeed = this._jumpStep / this._jumpTime;this.node.getPosition(this._curPos);Vec3.add(this._targetPos, this._curPos, new Vec3(this._jumpStep, 0, 0));if (this.CocosAnim) {this.CocosAnim.getState('cocos_anim_jump').speed = 3.5; //跳跃动画时间比较长,这里加速播放this.CocosAnim.play('cocos_anim_jump'); //播放跳跃动画}if (this.BodyAnim) {if (step === 1) {this.BodyAnim.play('oneStep');} else if (step === 2) {this.BodyAnim.play('twoStep');}}this._curMoveIndex += step;}onOnceJumpEnd() {if (this.CocosAnim) {this.CocosAnim.play('cocos_anim_idle');}this.node.emit('JumpEnd', this._curMoveIndex);}update (deltaTime: number) {if (this._startJump) {this._curJumpTime += deltaTime;if (this._curJumpTime > this._jumpTime) {// endthis.node.setPosition(this._targetPos);this._startJump = false;this.onOnceJumpEnd();} else {// tweenthis.node.getPosition(this._curPos);this._deltaPos.x = this._curJumpSpeed * deltaTime;Vec3.add(this._curPos, this._curPos, this._deltaPos);this.node.setPosition(this._curPos);}}}
}
4. 资源释放
不同场景的资源默认不会自动释放,如果都不释放的话内存所占会越来月高,最简单的方法就是再场景上勾上自动释放资源。
当然也可以防止特定资源被自动释放
具体操作 场景资源 · Cocos Creator
5. 导入资源
(1)资源工作流:
三种导入的方式:直接添加 复制 或者拖进来
不推荐直接在 操作系统的文件管理器 对资源文件进行操作,如有操作,请同步处理相应的 .meta
文件,如下建议:
- 关闭正在使用的编辑器,避免因为文件锁定或资源名称相同导致更新失败。
- 删除,重命名,移动资源时,请连同
.meta
文件一起删除,重命名,移动。 - 复制资源时如果连同
.meta
文件一起复制,将直接使用复制进来的.meta
文件,而不是再生成新的.meta
文件;如果只复制了资源文件,则会生成对应名称的新的.meta
文件。
(2)图像资源
常用的图片资源类型:
纹理贴图资源(Texture)
纹理贴图资源是一种用于程序采样的资源,如模型上的贴图、精灵上的 UI。当程序渲染 UI 或者模型时,会使用纹理坐标获取纹理颜色,然后填充在模型网格上,再加上光照等等一系列处理便渲染出了整个场景。
----Texture2D
Texture2D 是纹理贴图资源的一种,通常用于 3D 模型的渲染,如模型材质中的反射贴图、环境光遮罩贴图等等。
在将图像资源 导入 到 Creator 后,即可在 属性检查器 面板将其设置为 texture 类型,texture 类型便是 Texture2D 纹理资源。
----genMipmaps
为了加快 3D 场景渲染速度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的序列,这样的贴图被称为 mipmap。mipmap 中每一个层级的小图都是原图的一个特定比例的缩小细节的复制品,当贴图被缩小或者只需要从远距离观看时,mipmap 就会转换到适当的层级。
当 Texture2D 的 Mip Filter 属性设置为 nearest 或者 linear 时,会在两个相近的层级之间插值,自动生成 mipmap(仅对非压缩格式生效)。因为渲染远距离物体时,mipmap 贴图比原图小,提高了显卡采样过程中的缓存命中率,所以渲染的速度得到了提升。同时因为 mipmap 的小图精度较低,从而减少了摩尔纹现象,可以减少画面上的锯齿。另外因为额外生成了一些小图,所以 mipmap 需要额外占用约三分之一的内存空间。
精灵帧资源(SpriteFrame)
Sprite 组件剪裁相关设置详解
和图片裁剪相关的 Sprite 组件设置有以下两个:
Trim
勾选后将在渲染 Sprite 图像时去除图像周围的透明像素,我们将看到刚好能把图像包裹住的约束框。取消勾选,Sprite 节点的约束框会包括透明像素的部分。Size Mode
用来将节点的尺寸设置为原图或原图裁剪透明像素后的大小,通常用于在序列帧动画中保证图像显示为正确的尺寸。有以下几种选择:TRIMMED
选择该选项,会将节点的尺寸(size)设置为原始图片裁剪掉透明像素后的大小。RAW
选择该选项,会将节点尺寸设置为原始图片包括透明像素的大小。CUSTOM
自定义尺寸,用户在使用 矩形变换工具 拖拽改变节点的尺寸,或通过修改Size
属性,或在脚本中修改width
或height
后,都会自动将Size Mode
设为CUSTOM
。表示用户将自己决定节点的尺寸,而不需要考虑原始图片的大小。
立方体贴图
TextureCube 为立方体纹理,常用于设置场景的 天空盒。立方体贴图可以通过设置全景图 ImageAsset 为 TextureCube 类型获得,也可以在 Creator 中制作生成。
合成图集
在游戏中使用多张图片合成的图集作为美术资源,有以下优势:
- 合成图集时会去除每张图片周围的空白区域,加上可以在整体上实施各种优化算法,合成图集后可以大大减少游戏包体和内存占用
- 多个 Sprite 如果渲染的是来自同一张图集的图片时,这些 Sprite 可以使用同一个渲染批次来处理,大大减少 CPU 的运算时间,提高运行效率。
(3)预制件(Prefab)
预制件用于存储一些可以复用的场景对象,它可以包含节点、组件以及组件上的数据。由预制件生成的实例既可以继承模板的数据,又可以有自己定制化的数据修改。
预制资源 · Cocos Creator
二. 脚本使用
脚本指南及事件机制 · Cocos Creator
(1)编译环境 跳过
(2)组件装饰器
装饰器使用 · Cocos Creator
1.executeInEditMode
默认情况下,所有组件都只会在运行时执行,也就是说它们的生命周期回调在编辑器模式下并不会触发。executeInEditMode
允许当前组件在编辑器模式下运行,默认值为 false
。
2. requireComponent
requireComponent
参数用来指定当前组件的依赖组件,默认值为 null
。当组件添加到节点上时,如果依赖的组件不存在,引擎会自动将依赖组件添加到同一个节点,防止脚本出错。该选项在运行时同样有效。
3. executionOrder
executionOrder
用来指定脚本生命周期回调的执行优先级。小于 0 的脚本将优先执行,大于 0 的脚本将最后执行。排序方式如下:
- 对于同一节点上的不同组件,数值小的先执行,数值相同的按组件添加先后顺序执行
- 对于不同节点上的同一组件,按节点树排列决定执行的先后顺序
该优先级设定只对 onLoad
、onEnable
、start
、update
和 lateUpdate
有效,对 onDisable
和 onDestroy
无效。
4. 属性装饰器
import { _decorator, Component, Node, Color, RealCurve, Gradient } from 'cc';
const { ccclass, property, type, integer, float } = _decorator;@ccclass('HelloWord')
export class HelloWord extends Component {//加了注释会在cocos中图形化展示@type(Node)testNode: Node | null = null;@integertestInter = 2@floattestFloat = 3.6//颜色选择器// @property(Color)// color: Color//曲线选择器// @property(RealCurve)// realCurve: RealCurve = new RealCurve();//渐变色选择// @property(Gradient)// gradient = new Gradient();start() {console.log("hello world");}update(deltaTime: number) {}
}
一般情况下,属性是否显示在 属性检查器 中取决于属性名是否以 _
开头。如果是以 _
开头,则不显示。
如果要强制显示在 属性检查器 中,可以设置 visible
参数为 true:
@property({ visible: true })
private _num = 0;
(3)在脚本中访问节点
1. 获得组件所在的节点
start() {let node = this.node;node.setPosition(0.0, 0.0, 0.0);
}
2. 获得其它组件
如果你经常需要获得同一个节点上的其它组件,这就要用到 getComponent
这个 API,它会帮你查找你要的组件。
import { _decorator, Component, Label } from 'cc';
const { ccclass, property } = _decorator;@ccclass("test")
export class test extends Component {private label: any = nullstart() {this.label = this.getComponent(Label);let text = this.name + 'started';// Change the text in Label Componentthis.label.string = text;}
}
下面这俩等价
this.node.getComponent(Label) === this.getComponent(Label)
3. 查找子节点
有时候,游戏场景中会有很多个相同类型的对象,像是炮塔、敌人和特效,它们通常都有一个全局的脚本来统一管理。如果用 属性检查器 来一个一个将它们关联到这个脚本上,那工作就会很繁琐。为了更好地统一管理这些对象,我们可以把它们放到一个统一的父物体下,然后通过父物体来获得所有的子物体:
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;@ccclass("CannonManager")
export class CannonManager extends Component {start() {let cannons = this.node.children;//...}}
4. 节点常用api
(1)active属性 是否禁用该节点及其子节点。
(2)更改父节点
this.node.parent = parentNode;
this.node.removeFromParent(false);
parentNode.addChild(this.node);
(3)更改节点位置
使用
setPosition
方法:this.node.setPosition(100, 50, 100);
this.node.setPosition(new Vec3(100, 50, 100));
设置
position
变量:this.node.position = new Vec3(100, 50, 100)
(4)更改节点旋转
this.node.setRotation(90, 90, 90);
或通过欧拉角设置本地旋转:
this.node.setRotationFromEuler(90, 90, 90);
(5)更改节点缩放
this.node.setScale(2, 2, 2);
(6)新版本的计时器
比js那俩更灵活一些
使用计时器 · Cocos Creator
(7)组件执行优先级
组件和组件执行顺序 · Cocos Creator
大体分为两种方案:要么设置一个总的脚本去控制其他脚本的执行顺序,
要么在组件类上添加注释设置执行的order
(4)场景的加载和切换
在 Cocos Creator 中,我们使用场景文件名(不包含扩展名)来索引指代场景。并通过以下接口进行加载和切换操作:
director.loadScene("MyScene");
Asset Bundle 提供的 loadScene
只会加载指定 bundle 中的场景,并不会自动运行场景,还需要使用 director.runScene
来运行场景。
引擎同时只会运行一个场景,当切换场景时,默认会将场景内所有节点和其他实例销毁。如果我们需要用一个组件控制所有场景的加载,或在场景之间传递参数数据,就需要将该组件所在节点标记为「常驻节点」,使它在场景切换时不被自动销毁,常驻内存。我们使用以下接口:
director.addPersistRootNode(myNode);
上面的接口会将 myNode
变为常驻节点,这样挂在上面的组件都可以在场景之间持续作用,我们可以用这样的方法来储存玩家信息,或下一个场景初始化时需要的各种数据。 需要注意的是,目标节点必须为位于层级的根节点,否则设置无效。
如果要取消一个节点的常驻属性:
director.removePersistRootNode(myNode);
需要注意的是上面的 API 并不会立即销毁指定节点,只是将节点还原为可在场景切换时销毁的节点。
加载场景时,可以附加一个参数用来指定场景加载后的回调函数:
director.loadScene("MyScene", onSceneLaunched);
上一行里 onSceneLaunched
就是声明在本脚本中的一个回调函数,在场景加载后可以用来进一步的进行初始化或数据传递的操作。
由于回调函数只能写在本脚本中,所以场景加载回调通常用来配合常驻节点,在常驻节点上挂载的脚本中使用。
同样的我们也可以预加载场景
director.loadScene
会在加载场景之后自动切换运行新场景,有些时候我们需要在后台静默加载新场景,并在加载完成后手动进行切换。那就可以预先使用 preloadScene
接口对场景进行预加载:
director.preloadScene("table", function () {console.log('Next scene preloaded');
});
之后在合适的时间调用 loadScene
,就可以真正切换场景。
director.loadScene("table");
就算预加载还没完成,你也可以直接调用 director.loadScene
,预加载完成后场景就会启动。
(5)事件系统
Cocos Creator 引擎提供了 EventTarget
类,用以实现自定义事件的监听和发射,在使用之前,需要先从 'cc'
模块导入,同时需要实例化一个 EventTarget
对象。
import { EventTarget } from 'cc';
const eventTarget = new EventTarget();
这玩意熟悉发布订阅模式的随便虐
监听 on once 卸载 off 发布 emit
内置事件(这是新的得记住)
输入系统事件
输入事件系统 · Cocos Creator
节点事件
三, 功能模块
1. 图像渲染
(1). 渲染管线
Cocos Creator 3.1 的内置渲染管线包括 builtin-forward(前向渲染管线)和 builtin-deferred(延迟渲染管线)。渲染管线可通过编辑器主菜单中的 项目 -> 项目设置 -> 项目数据 -> 渲染管线 进行设置,设置完成之后 重启编辑器 即可生效。
延迟的兼容性不是特别好,对于卡通材质的绘制也有问题,实验阶段只能说是。
(2)相机
相机 · Cocos Creator
(3) 光照
光学度量单位(Photometric Unit) 是用来计算光的强弱(大小)和方向的一门科学:
光通量(Luminous Flux)
单位 流明(lm),单位时间内光源所发出或者被照物体所接收的总光能。改变光源大小不会影响场景照明效果。
亮度(Luminance)
单位 坎德拉每平方米(cd/m2),单位面积光源在给定方向上,在每单位面积内所发出的总光通量。改变光源大小会影响场景照明效果。
照度(Illuminance)
单位 勒克斯(lux 或 lx),每单位面积所接收到的光通量。该值受光的传播距离影响,对于同样光源而言,当光源的距离为原先的两倍时,照度减为原先的四分之一,呈平方反比关系。
在真实世界中,由于描述光源的重要物理参数不一样,我们通常用 光通量(Luminous Flux) 和 亮度(Luminance) 来描述生活中常见的带有照明面积的光源,用 照度(Illuminance) 来描述太阳光。
光源的类型:
平行光:
平行光又称为方向光(Directional Light),是最常用的一种光源,模拟了无限远处的光源发出的光线,常用于实现太阳光。
球面光:
球面光会向所有方向均匀地发散光线,接近于蜡烛产生的光线。物体受到的光照强度会随着跟光源距离的增大而减弱,当距离超过设置的光照影响范围,则光照强度为 0。
在实际应用中可用于模拟火把、蜡烛、灯泡等光源,照亮四周一定距离内的环境。
聚光灯:
聚光灯 是由一个点向一个方向发射一束锥形光线,类似于手电筒或舞台照明灯产生的光线。与其他光源相比,聚光灯多了 SpotAngle
属性,用于调整聚光灯的光照范围
环境光
在生活中,错综复杂的光线与凹凸不平的物体表面相互反射,使得整个环境都被照亮,仿佛被一层光均匀笼罩,这个光一般称为 环境光,也称为 漫射环境光。
因为环境光可以均匀地照亮场景中的所有物体,常用于解决模型背光面全黑的问题,一般需要配合其他类型的光源一起使用。例如场景中只有一个平行光,那么在模型的背光源处会显得非常暗,加入环境光则可以提升模型背部的亮度,显得更加美观。
(4)阴影
在 3D 世界中,光与影一直都是极其重要的组成部分,它们能够丰富整个环境,质量好的阴影可以达到以假乱真的效果,并且使得整个世界具有立体感。
Creator 3.0 目前支持 Planar 和 ShadowMap 两种阴影类型。
(5)光照贴图
烘焙系统会对光源稳定的静态物体所受到的光照和阴影等进行预先计算,计算产生的结果存放在一张纹理贴图中,这张贴图我们称之为 光照贴图。
生成的光照贴图 Creator 会在运行时自动处理并使用。在光源固定的场景中,使用光照贴图代替实时的光照计算,可以减少资源消耗,从而提高场景运行效率。
2. 网格
(1)MeshRenderer
(网格渲染器)组件用于显示一个静态的 3D 模型。通过 Mesh 属性设置模型网格,通过 Materials 属性控制模型的显示外观。
在 属性检查器 中点击 添加组件 -> Mesh -> MeshRenderer 即可添加 MeshRenderer 组件。
(2)蒙皮网格渲染器组件(SkinnedMeshRenderer)
蒙皮网格渲染器组件(SkinnedMeshRenderer)主要用于渲染蒙皮模型网格。
导入模型资源 后,若模型网格中带有蒙皮信息,在使用模型时,SkinnedMeshRenderer 组件便会自动添加到模型节点上。
CocosCreator + JavaScript游戏开发相关推荐
- JavaScript游戏开发(3)(笔记)
文章目录 七.支持移动设备的横向卷轴游戏 准备 7.1 角色的简单移动 7.2 背景 7.3 加入敌人与帧数控制 7.4 碰撞.计分.重新开始 7.5 手机格式 7.6 全屏模式 7.7 存在的问题 ...
- JavaScript游戏开发简介
This full tutorial video is an introduction to game development using vanilla JavaScript and HTML5 c ...
- [JavaScript游戏开发] 绘制冰宫宝藏地图、人物鼠标点击移动、障碍检测
系列文章目录 第一章 2D二维地图绘制.人物移动.障碍检测 第二章 跟随人物二维动态地图绘制.自动寻径.小地图显示(人物红点显示) 第三章 绘制冰宫宝藏地图.人物鼠标点击移动.障碍检测 第四章 绘制Q ...
- 用于游戏开发的 8 大 JavaScript 引擎
JavaScript 对于游戏开发初学者来说是一门优秀的语言,并且有很多框架可以帮助你. 开发游戏无疑是一项艰苦的工作,但您可以使用正确的工具来减轻痛苦. 值得庆幸的是,与其他引擎相比,JavaScr ...
- 25 个超棒的 HTML5 JavaScript 游戏引擎开发库
就像在汽车中,引擎完成主要的工作,使汽车看起来不可思议.游戏引擎同理,游戏开发者完成细节的工作,使游戏看起来真实.吸引人眼球.游戏引擎负责其余的事情.早期,游戏开发者通常从草图做起,花费高昂,且不容易 ...
- 微信小游戏开发零基础教程(一)-CocosCreator
微信小游戏零基础教程(一)-CocosCreator 最终效果预览 准备工作 创建工程 测试第一个场景 最终效果预览 本教程最后制作的游戏效果: 准备工作 下载 微信开发者工具 最新版->下载地 ...
- python html5游戏_25 个超棒的 HTML5 JavaScript 游戏引擎开发库
就像在汽车中,引擎完成主要的工作,使汽车看起来不可思议.游戏引擎同理,游戏开发者完成细节的工作,使游戏看起来真实.吸引人眼球.游戏引擎负责其余的事情.早期,游戏开发者通常从草图做起,花费高昂,且不容易 ...
- 游戏开发的HelloWorld,快速入门,新手上路,使用CocosCreator+JS,flyBird(飞翔小鸟)
游戏开发的HelloWorld,快速入门,新手上路,使用CocosCreator+JS,flyBird(飞翔小鸟) 介绍 开发环境 游戏原理 文件结构 详细操作 全部代码 介绍 拓展什么的都没有,真正 ...
- HTML5游戏开发进阶指南(亚马逊5星畅销书,教你用HTML5和JavaScript构建游戏!)
HTML5游戏开发进阶指南(亚马逊5星畅销书,教你用HTML5和JavaScript构建游戏!) [印]香卡(Shankar,A.R.) 著 谢光磊 译 ISBN 978-7-121-21226-0 ...
最新文章
- 微信小程序云开发用户身份登录_云开发版的微信商城小程序第一章
- 汽车全景标定(拼接)效果的检验方法
- 一到关于js函数的前端面试题引发的血案
- vue 给标签添加data属性_vue之data属性
- [BZOJ1503][NOI2004]郁闷的出纳员 无旋Treap
- div.2/C. They Are Everywheretwo pointer
- SQL Server 2008修改数据库为多用户模式
- jmeter分布式步骤
- Delphi学习之函数 ⑨汉字拼音功能函数
- winform 报表的基本使用
- Qt5+STM32F407+步进电机 | 通过电脑控制步进电机实现:6+2通道、速度可变、运动精确步数的教程——基础知识(2/4)
- 卡尔曼滤波c语言程序,卡尔曼滤波算法及C语言实现
- 【转】打造个性化ghostxp光盘另类教程(2)
- 基于用户行为分析建立用户偏好模型
- 戈登贝尔奖是超级计算机应用的最高奖,实现零突破!中国获高性能计算应用领域最高奖戈登贝尔奖...
- vscode配置c/c++编译环境(最终解决办法)
- 车载以太网转换器 100/1000BASE-T1 转换器
- 【OpenCV学习笔记】之图像金字塔(Image Pyramid)
- 深度学习之图像分类(十六)-- EfficientNetV2 网络结构
- idea 代码格式化 谷歌_Google Docsmaklet格式化代码