前提
無論學(xué)什么技術(shù)知識,官方文檔都應(yīng)該是你第一個(gè)教程,所以請先至少閱讀新手上路這一節(jié) http://docs.cocos.com/creator/manual/zh/getting-started/ 再來看這篇文章。
這里假設(shè)你已經(jīng)安裝成功了 Cocos Creator。
TypeScript VS JavaScript
這里當(dāng)然只會(huì)講優(yōu)點(diǎn):
1. ts 是 js 的超集,所有 js 的語法 ts 都支持。
2. ts 支持接近完美的代碼提示,js 代碼提示接近于沒有。
3. ts 有類型定義,編譯時(shí)就可以排除很多無意義的錯(cuò)誤。
4. ts 可以重構(gòu),適合大型項(xiàng)目。
5. ts 可以使用 es6 async之類的所有新語法。而 js Cocos Creator 還沒有完全支持es6。
6. 最重要的一點(diǎn):我以后的教程都會(huì)用 ts 寫,如果你不用 ts,你就會(huì)永遠(yuǎn)失去我了。
代碼編輯器選擇
這里只推薦兩個(gè):
- Visual Studio Code
- WebStorm
vs code 的優(yōu)點(diǎn)是快,與cocos creator 結(jié)合的好,一些功能需要自己安裝插件。
webstorm 的優(yōu)點(diǎn)是所有你想要的功能都先天內(nèi)置了,缺點(diǎn)是占內(nèi)存,個(gè)人感覺還有點(diǎn)丑。
對于我自己來說,我在公司用 WebStorm,在家用 VS Code。
如果你還是不知道用哪個(gè),我只能先推薦你用VS Code 因?yàn)橄旅娴膬?nèi)容是面向VS Code。
學(xué)習(xí) TypeScript
既然要用ts開發(fā)游戲,肯定要知道ts的語法,我這一篇文章不可能把所有ts的語法都講完,所以https://www.tslang.cn/docs/home.html,當(dāng)然,不一定要一次性全看完,你可以先看個(gè)大概,遇到問題再補(bǔ)習(xí)。
TypeScript 環(huán)境配置
任意打開一個(gè)項(xiàng)目,把這幾個(gè)都點(diǎn)一遍
控制臺會(huì)輸出
打開編輯器,你會(huì)發(fā)現(xiàn)一個(gè)名字為 creator.d.ts 的腳本
creator 的提示都依靠這個(gè)腳本,引擎的api變動(dòng)也要及時(shí)更新這個(gè)腳本,所以每次更新引擎的時(shí)候都要重新點(diǎn)一次上面那個(gè)“更新VS Code只能提示數(shù)據(jù)“來重新生成creator.d.ts。
資源管理器右鍵新建一個(gè)ts腳本,點(diǎn)開后你會(huì)發(fā)現(xiàn)有很多沒用的東西,而且還會(huì)有一個(gè)提示錯(cuò)誤(1.81)。。。
// - [English] http://www.cocos2d-x.org/docs/editors_and_tools/creator-chapters/scripting/typescript/index.html // Learn Attribute: // - [Chinese] http://www.cocos.com/docs/creator/scripting/reference/attributes.html // - [English] http://www.cocos2d-x.org/docs/editors_and_tools/creator-chapters/scripting/reference/attributes/index.html // Learn life-cycle callbacks: // - [Chinese] http://www.cocos.com/docs/creator/scripting/life-cycle-callbacks.html // - [English] http://www.cocos2d-x.org/docs/editors_and_tools/creator-chapters/scripting/life-cycle-callbacks/index.html const {ccclass, property} = cc._decorator; @ccclass export default class NewClass extends cc.Component { @property(cc.Label) label: cc.Label = null; @property text: string = "hello"; // LIFE-CYCLE CALLBACKS: // onLoad () {}, start () { }, // update (dt) {}, }
編輯器右上角“打開程序安裝路徑“,
static-》template-》new-script.ts
這個(gè)腳本就是新建ts腳本的默認(rèn)樣式,我們來重新編輯一下,編輯后的腳本如下
const {ccclass, property} = cc._decorator; @ccclass export class NewClass extends cc.Component { }
重新新建一個(gè)ts腳本,你會(huì)發(fā)現(xiàn)跟剛才編輯的默認(rèn)腳本是一個(gè)樣子了。
配置自己的聲明文件
以d.ts為后綴名的文件,會(huì)被識別為聲明文件,creator.d.ts是引擎的聲明文件,我們也可以定義自己的聲明文件,需要注意的是聲明文件要放在assets文件外,因?yàn)閍ssets文件里的腳本都會(huì)被引擎編譯,而聲明文件的作用就是寫代碼時(shí)提示一下,編譯之后就不需要了。
舉個(gè)栗子
在項(xiàng)目的根目錄添加一個(gè)global.d.ts文件
然后在項(xiàng)目里的腳本就可以得到對應(yīng)的提示
更多類型定義戳 https://www.tslang.cn/docs/handbook/declaration-files/introduction.html
屬性類型聲明
const LEVEL = cc.Enum({EASY:1,HARD:2}); @ccclass export class Game extends cc.Component { // 整型 @property(cc.Integer) intVar: number = 0; // 浮點(diǎn)型 @property(cc.Float) floatVar: number = 0; // 布爾型 @property(cc.Boolean) boolVar: boolean = false; // 節(jié)點(diǎn) @property(cc.Node) nodeVar: cc.Node = null; // 節(jié)點(diǎn)數(shù)組 @property([cc.Node]) nodeArrVar: Array<cc.Node> = []; // Label @property(cc.Label) labelVar: cc.Label = null; // 預(yù)制體 @property(cc.Prefab) prefabVar: cc.Prefab = null; // 點(diǎn) @property(cc.Vec2) vec2Var: cc.Vec2 = cc.v2(); // 自定義節(jié)點(diǎn) @property(Player) palyerVar: Player = null; // 重點(diǎn)來了,自定義枚舉 /** * 全局變量 * const LEVEL = cc.Enum({EASY:1,HARD:2}); */ @property({ type:LEVEL }) enumVa = LEVEL.EASY; }
用 TypeScript 寫一個(gè)游戲
最后我們來切身體會(huì)一下TypeScript的柔軟絲滑。
挑一個(gè)熟悉的游戲來寫,官方文檔里有一個(gè)摘星星的游戲,我們用Ts重新寫一下。
第一步:新建一個(gè)工程
第二步:寫幾個(gè)腳本
Game.ts
import { Player } from "./Player"; const { property, ccclass } = cc._decorator; @ccclass export class Game extends cc.Component { // 這個(gè)屬性引用了星星的預(yù)制資源 @property(cc.Prefab) private starPrefab: cc.Prefab = null; // 星星產(chǎn)生后消失時(shí)間的隨機(jī)范圍 @property(cc.Integer) private maxStarDuration = 0; @property(cc.Integer) private minStarDuration = 0 // 地面節(jié)點(diǎn),用于確定星星生成的高度 @property(cc.Node) private groundNode: cc.Node = null; // player 節(jié)點(diǎn),用于獲取主角彈跳的高度,和控制主角行動(dòng)開關(guān) @property(cc.Node) public playerNode: cc.Node = null; // score label 的引用 @property(cc.Label) private scoreLabel: cc.Label = null; // 得分音效資源 @property(cc.AudioClip) private scoreAudio: cc.AudioClip = null; // 地面節(jié)點(diǎn)的Y軸坐標(biāo) private groundY: number; // 定時(shí)器 public timer: number; // 星星存在的持續(xù)時(shí)間 public starDuration: number; // 當(dāng)前分?jǐn)?shù) private score: number; protected onLoad() { // 獲取地平面的 y 軸坐標(biāo) this.groundY = this.groundNode.y + this.groundNode.height / 2; // 初始化計(jì)時(shí)器 this.timer = 0; this.starDuration = 0; // 生成一個(gè)新的星星 this.spawnNewStar(); // 初始化計(jì)分 this.score = 0; } // 生成一個(gè)新的星星 public spawnNewStar() { // 使用給定的模板在場景中生成一個(gè)新節(jié)點(diǎn) let newStar = cc.instantiate(this.starPrefab); // 將新增的節(jié)點(diǎn)添加到 Canvas 節(jié)點(diǎn)下面 this.node.addChild(newStar); // 為星星設(shè)置一個(gè)隨機(jī)位置 newStar.setPosition(this.getNewStarPosition()); // 將 Game 組件的實(shí)例傳入星星組件 newStar.getComponent("Star").init(this); // 重置計(jì)時(shí)器 this.starDuration = this.minStarDuration + cc.random0To1() * (this.maxStarDuration - this.minStarDuration); this.timer = 0; } // 新星星的位置 public getNewStarPosition() { let randX = 0; // 根據(jù)地平面位置和主角跳躍高度,隨機(jī)得到一個(gè)星星的 y 坐標(biāo) let randY = this.groundY + cc.random0To1() * this.playerNode.getComponent("Player").jumpHeight + 50; // 根據(jù)屏幕寬度,隨機(jī)得到一個(gè)星星 x 坐標(biāo) let maxX = this.node.width / 2; randX = cc.randomMinus1To1() * maxX; // 返回星星坐標(biāo) return cc.p(randX, randY); } // called every frame protected update(dt: number) { // 每幀更新計(jì)時(shí)器,超過限度還沒有生成新的星星 // 就會(huì)調(diào)用游戲失敗邏輯 if (this.timer > this.starDuration) { this.gameOver(); return; } this.timer += dt; } // 得分 public gainScore() { this.score += 1; // 更新 scoreDisplay Label 的文字 this.scoreLabel.string = "Score: " + this.score.toString(); // 播放得分音效 // 不加as any就會(huì)報(bào)錯(cuò),不信你試試 cc.audioEngine.play(this.scoreAudio as any, false, 1); } // gg private gameOver() { this.playerNode.stopAllActions(); //停止 player 節(jié)點(diǎn)的跳躍動(dòng)作 cc.director.loadScene("game"); } }
Player.ts
const { ccclass, property } = cc._decorator; @ccclass export class Player extends cc.Component { // 主角跳躍高度 @property(cc.Integer) private jumpHeight: number = 0; // 主角跳躍持續(xù)時(shí)間 @property(cc.Integer) private jumpDuration: number = 0; // 最大移動(dòng)速度 @property(cc.Integer) private maxMoveSpeed: number = 0; // 加速度 @property(cc.Integer) private accel: number = 0; // 跳躍音效資源 @property(cc.AudioClip) private jumpAudio: cc.AudioClip = null; private xSpeed: number = 0; private accLeft: boolean = false; private accRight: boolean = false; private jumpAction: cc.Action = null; private setJumpAction() { // 跳躍上升 let jumpUp = cc.moveBy(this.jumpDuration, cc.p(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // 下落 let jumpDown = cc.moveBy(this.jumpDuration, cc.p(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // 添加一個(gè)回調(diào)函數(shù),用于在動(dòng)作結(jié)束時(shí)調(diào)用我們定義的其他方法 let callback = cc.callFunc(this.playJumpSound, this); // 不斷重復(fù),而且每次完成落地動(dòng)作后調(diào)用回調(diào)來播放聲音 return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback)); } private playJumpSound() { // 調(diào)用聲音引擎播放聲音 cc.audioEngine.play(this.jumpAudio as any, false, 1); } private addEventListeners() { cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); cc.find("Canvas").on(cc.Node.EventType.TOUCH_START, this.onScreenTouchStart,this); cc.find("Canvas").on(cc.Node.EventType.TOUCH_CANCEL, this.onScreenTouchEnd, this); cc.find("Canvas").on(cc.Node.EventType.TOUCH_END, this.onScreenTouchEnd,this); } private moveLeft() { this.accLeft = true; this.accRight = false; } private moveRight() { this.accLeft = false; this.accRight = true; } private stopMove() { this.accLeft = false; this.accRight = false; } private onScreenTouchStart(event: cc.Event.EventTouch) { if (event.getLocationX() > cc.winSize.width/2) { this.moveRight(); } else { this.moveLeft(); } } private onScreenTouchEnd() { this.stopMove(); } private onKeyDown(event: cc.Event.EventKeyboard) { switch ((event as any).keyCode) { case cc.KEY.a: case cc.KEY.left: this.moveLeft(); break; case cc.KEY.d: case cc.KEY.right: this.moveRight(); break; } } private onKeyUp(event: cc.Event.EventKeyboard) { switch ((event as any).keyCode) { case cc.KEY.a: case cc.KEY.left: this.stopMove(); break; case cc.KEY.d: case cc.KEY.right: this.stopMove(); break; } } // use this for initialization protected onLoad() { // 初始化跳躍動(dòng)作 this.jumpAction = this.setJumpAction(); this.node.runAction(this.jumpAction); // 加速度方向開關(guān) this.accLeft = false; this.accRight = false; // 主角當(dāng)前水平方向速度 this.xSpeed = 0; // 初始化輸入監(jiān)聽 this.addEventListeners(); } // called every frame protected update(dt: number) { // 根據(jù)當(dāng)前加速度方向每幀更新速度 if (this.accLeft) { this.xSpeed -= this.accel * dt; } else if (this.accRight) { this.xSpeed += this.accel * dt; } // 限制主角的速度不能超過最大值 if (Math.abs(this.xSpeed) > this.maxMoveSpeed) { // if speed reach limit, use max speed with current direction this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed); } // 根據(jù)當(dāng)前速度更新主角的位置 this.node.x += this.xSpeed * dt; if (this.node.x <= -this.node.parent.width / 2) { this.node.x = this.node.parent.width / 2; } if (this.node.x > this.node.parent.width / 2) { this.node.x = -this.node.parent.width / 2; } } }
Star.ts
import { Game } from "./Game"; const {ccclass,property} = cc._decorator; @ccclass export class Star extends cc.Component { // 星星和主角之間的距離小雨這個(gè)數(shù)值時(shí),就會(huì)完成收集 @property(cc.Integer) private pickRadius: number = 0; private game: Game = null; public init(game:Game) { this.game = game; } getPlayerDistance() { // 根據(jù) player 節(jié)點(diǎn)位置判斷距離 let playerPos = this.game.playerNode.getPosition(); // 根據(jù)兩點(diǎn)位置計(jì)算兩點(diǎn)之間距離 let dist = cc.pDistance(this.node.position, playerPos); return dist; } onPicked() { // 當(dāng)星星被收集時(shí),調(diào)用 Game 腳本中的接口,生成一個(gè)新的星星 this.game.spawnNewStar(); // 調(diào)用 Game 腳本的得分方法 this.game.gainScore(); // 然后銷毀當(dāng)前星星節(jié)點(diǎn) this.node.destroy(); } // called every frame update(dt:number) { // 每幀判斷和主角之間的距離是否小于收集距離 if (this.getPlayerDistance() < this.pickRadius) { // 調(diào)用收集行為 this.onPicked(); return; } // 根據(jù) Game 腳本中的計(jì)時(shí)器更新星星的透明度 let opacityRatio = 1 - this.game.timer/this.game.starDuration; let minOpacity = 50; this.node.opacity = minOpacity + Math.floor(opacityRatio * (255 - minOpacity)); } }
以上就是CocosCreator入門教程之用TS制作第一個(gè)游戲的詳細(xì)內(nèi)容,更多關(guān)于CocosCreator TS制作游戲的資料請關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://blog.csdn.net/potato47/article/details/79254524