Cocos Creator + TypeScript 入门教程_new-script.ts-程序员宅基地

技术标签: Cocos Creator  Cocos Creator实战教程  

这不是 Cocos Creator 的入门教程,也不是TypeScript 的入门教程,这是 Cocos Creator+TypeScript 的入门教程。


前提

无论学什么技术知识,官方文档都应该是你第一个教程,所以请先至少阅读新手上路这一节 http://docs.cocos.com/creator/manual/zh/getting-started/ 再来看这篇文章。

这里假设你已经安装成功了 Cocos Creator

TypeScript VS JavaScript

这里当然只会讲优点:
1. ts 是 js 的超集,所有 js 的语法 ts 都支持。
2. ts 支持接近完美的代码提示,js 代码提示接近于没有。
3. ts 有类型定义,编译时就可以排除很多无意义的错误。
4. ts 可以重构,适合大型项目。
5. ts 可以使用 es6 async之类的所有新语法。而 js Cocos Creator 还没有完全支持es6。
6. 最重要的一点:我以后的教程都会用 ts 写,如果你不用 ts,你就会永远失去我了

代码编辑器选择

这里只推荐两个:

  1. Visual Studio Code
  2. WebStorm

vs code 的优点是快,与cocos creator 结合的好,一些功能需要自己安装插件。

webstorm 的优点是所有你想要的功能都先天内置了,缺点是占内存,个人感觉还有点丑。

对于我自己来说,我在公司用 WebStorm,在家用 VS Code。

如果你还是不知道用哪个,我只能先推荐你用VS Code 因为下面的内容是面向VS Code。

学习 TypeScript

既然要用ts开发游戏,肯定要知道ts的语法,我这一篇文章不可能把所有ts的语法都讲完,所以https://www.tslang.cn/docs/home.html,当然,不一定要一次性全看完,你可以先看个大概,遇到问题再补习。

TypeScript 环境配置

任意打开一个项目,把这几个都点一遍
这里写图片描述

控制台会输出
这里写图片描述

打开编辑器,你会发现一个名字为 creator.d.ts 的脚本
这里写图片描述
creator 的提示都依靠这个脚本,引擎的api变动也要及时更新这个脚本,所以每次更新引擎的时候都要重新点一次上面那个“更新VS Code只能提示数据“来重新生成creator.d.ts。

资源管理器右键新建一个ts脚本,点开后你会发现有很多没用的东西,而且还会有一个提示错误(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
这个脚本就是新建ts脚本的默认样式,我们来重新编辑一下,编辑后的脚本如下

const {ccclass, property} = cc._decorator;

@ccclass
export class NewClass extends cc.Component {
    

}

重新新建一个ts脚本,你会发现跟刚才编辑的默认脚本是一个样子了。

配置自己的声明文件

以d.ts为后缀名的文件,会被识别为声明文件,creator.d.ts是引擎的声明文件,我们也可以定义自己的声明文件,需要注意的是声明文件要放在assets文件外,因为assets文件里的脚本都会被引擎编译,而声明文件的作用就是写代码时提示一下,编译之后就不需要了。

举个栗子
在项目的根目录添加一个global.d.ts文件
这里写图片描述
然后在项目里的脚本里就可以得到对应的提示
这里写图片描述

这里写图片描述

更多类型定义戳 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;
    // 浮点型
    @property(cc.Float)
    floatVar: number = 0;
    // 布尔型
    @property(cc.Boolean)
    boolVar: boolean = false;
    // 节点
    @property(cc.Node)
    nodeVar: cc.Node = null;
    // 节点数组
    @property([cc.Node])
    nodeArrVar: Array<cc.Node> = [];
    // Label
    @property(cc.Label)
    labelVar: cc.Label = null;
    // 预制体
    @property(cc.Prefab)
    prefabVar: cc.Prefab = null;
    // 点
    @property(cc.Vec2)
    vec2Var: cc.Vec2 = cc.v2();
    // 自定义节点
    @property(Player)
    palyerVar: Player = null;
    // 重点来了,自定义枚举
    /**
     * 全局变量
     * const LEVEL = cc.Enum({EASY:1,HARD:2});
     */ 
    @property({
        type:LEVEL
    })
    enumVa = LEVEL.EASY;
}

这里写图片描述

用 TypeScript 写一个游戏

最后我们来切身体会一下TypeScript的柔软丝滑。

挑一个熟悉的游戏来写,官方文档里有一个摘星星的游戏,我们用Ts重新写一下。

第一步:新建一个工程
这里写图片描述

第二步:写几个脚本

Game.ts

import { Player } from "./Player";

const { property, ccclass } = cc._decorator;

@ccclass
export class Game extends cc.Component {
    // 这个属性引用了星星的预制资源
    @property(cc.Prefab)
    private starPrefab: cc.Prefab = null;
    // 星星产生后消失时间的随机范围
    @property(cc.Integer)
    private maxStarDuration = 0;
    @property(cc.Integer)
    private minStarDuration = 0
    // 地面节点,用于确定星星生成的高度
    @property(cc.Node)
    private groundNode: cc.Node = null;
    // player 节点,用于获取主角弹跳的高度,和控制主角行动开关
    @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;

    // 地面节点的Y轴坐标
    private groundY: number;
    // 定时器
    public timer: number;
    // 星星存在的持续时间
    public starDuration: number;
    // 当前分数
    private score: number;

    protected onLoad() {
        // 获取地平面的 y 轴坐标
        this.groundY = this.groundNode.y + this.groundNode.height / 2;
        // 初始化计时器
        this.timer = 0;
        this.starDuration = 0;
        // 生成一个新的星星
        this.spawnNewStar();
        // 初始化计分
        this.score = 0;
    }

    // 生成一个新的星星
    public spawnNewStar() {
        // 使用给定的模板在场景中生成一个新节点
        let newStar = cc.instantiate(this.starPrefab);
        // 将新增的节点添加到 Canvas 节点下面
        this.node.addChild(newStar);
        // 为星星设置一个随机位置
        newStar.setPosition(this.getNewStarPosition());
        // 将 Game 组件的实例传入星星组件
        newStar.getComponent('Star').init(this);
        // 重置计时器
        this.starDuration = this.minStarDuration + cc.random0To1() * (this.maxStarDuration - this.minStarDuration);
        this.timer = 0;
    }

    // 新星星的位置
    public getNewStarPosition() {
        let randX = 0;
        // 根据地平面位置和主角跳跃高度,随机得到一个星星的 y 坐标
        let randY = this.groundY + cc.random0To1() * this.playerNode.getComponent('Player').jumpHeight + 50;
        // 根据屏幕宽度,随机得到一个星星 x 坐标
        let maxX = this.node.width / 2;
        randX = cc.randomMinus1To1() * maxX;
        // 返回星星坐标
        return cc.p(randX, randY);
    }

    // called every frame
    protected update(dt: number) {
        // 每帧更新计时器,超过限度还没有生成新的星星
        // 就会调用游戏失败逻辑
        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就会报错,不信你试试
        cc.audioEngine.play(this.scoreAudio as any, false, 1);
    }

    // gg
    private gameOver() {
        this.playerNode.stopAllActions(); //停止 player 节点的跳跃动作
        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;
    // 主角跳跃持续时间
    @property(cc.Integer)
    private jumpDuration: number = 0;
    // 最大移动速度
    @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());
        // 添加一个回调函数,用于在动作结束时调用我们定义的其他方法
        let callback = cc.callFunc(this.playJumpSound, this);
        // 不断重复,而且每次完成落地动作后调用回调来播放声音
        return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback));
    }

    private playJumpSound() {
        // 调用声音引擎播放声音
        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() {
        // 初始化跳跃动作
        this.jumpAction = this.setJumpAction();
        this.node.runAction(this.jumpAction);

        // 加速度方向开关
        this.accLeft = false;
        this.accRight = false;
        // 主角当前水平方向速度
        this.xSpeed = 0;

        // 初始化输入监听
        this.addEventListeners();
    }

    // called every frame
    protected update(dt: number) {
        // 根据当前加速度方向每帧更新速度
        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);
        }

        // 根据当前速度更新主角的位置
        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 {
    

    // 星星和主角之间的距离小雨这个数值时,就会完成收集
    @property(cc.Integer)
    private pickRadius: number = 0;
    private game: Game = null;

    public init(game:Game) {
        this.game = game;
    }

    getPlayerDistance() {
        // 根据 player 节点位置判断距离
        let playerPos = this.game.playerNode.getPosition();
        // 根据两点位置计算两点之间距离
        let dist = cc.pDistance(this.node.position, playerPos);
        return dist;
    }

    onPicked() {
        // 当星星被收集时,调用 Game 脚本中的接口,生成一个新的星星
        this.game.spawnNewStar();
        // 调用 Game 脚本的得分方法
        this.game.gainScore();
        // 然后销毁当前星星节点
        this.node.destroy();
    }

    // called every frame
    update(dt:number) {
        // 每帧判断和主角之间的距离是否小于收集距离
        if (this.getPlayerDistance() < this.pickRadius) {
            // 调用收集行为
            this.onPicked();
            return;
        }
        // 根据 Game 脚本中的计时器更新星星的透明度
        let opacityRatio = 1 - this.game.timer/this.game.starDuration;
        let minOpacity = 50;
        this.node.opacity = minOpacity + Math.floor(opacityRatio * (255 - minOpacity));
    }

}

第三步:可以玩了【github pages发布教程传送门

https://potato47.github.io/star-catcher/

这后面的进度有点快,同学们自己消化一下,老师去吃饭了。


参考:http://forum.cocos.com/t/typescript-creator-github/42200
http://docs.cocos.com/creator/manual/zh/scripting/typescript.html
https://www.tslang.cn/docs/home.html
源码:https://github.com/potato47/star-catcher
原文:http://blog.csdn.net/potato47/article/details/79254524

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/potato47/article/details/79254524

智能推荐

代码的坏味道之十五 :Message Chains(过度耦合的消息链)_面向对象的代码异味messagechain-程序员宅基地

文章浏览阅读1.7k次。如果你看到用户向一个对象索求(request)另一个对象,然后再向后者索求另一个对象,然后再索求另一个对象……这就是Message Chains。实际代码中你看到的可 能是一长串getThis()或一长串临时变量。采取这种方式,意味客户将与查找过程中的航行结构(structure of the navigation)紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。这时候_面向对象的代码异味messagechain

自用PTA题目记录0004_作为一个职业hr,要给某个项目组的所有职员群发邮件,已经知道hr有所有人的姓名、电-程序员宅基地

文章浏览阅读1.7k次。自用PTA题目记录0004 以下题目序号并无实际意义文章目录自用PTA题目记录00047-2 谷歌的招聘代码总结7-2 谷歌的招聘题目作者: AMi 单位: 临沂大学 代码长度限制: 16 KB 时间限制: 200 ms 内存限制: 64 MB作为一个职业HR,要给某个项目组的所有职员群发邮件,已经知道HR有所有人的姓名、电话以及邮箱,请编写程序,取出所有人的邮箱,合并为一个邮箱列表,邮箱之间以“;”隔开,使其可以群发邮件。输入格式: 在一行中给出所有人的姓名、电话以及邮箱,例如:_作为一个职业hr,要给某个项目组的所有职员群发邮件,已经知道hr有所有人的姓名、电

安装与使用memcached+php_php安装mem-程序员宅基地

文章浏览阅读318次。我们想使用PHP+memcache的开发组合选择一个专门的memcache机器,要么单独准备,要么将它和php放在一起(简略版)。1.安装# 安装依赖# yum -y install libevent-devel# 下载与安装# cd /software# wget http://memcached.org/files/memcached-1.5.20.tar.gz# t..._php安装mem

JS 读取并解析csv文件 —— 前端踩坑13_js读取csv文件内容-程序员宅基地

文章浏览阅读9.5k次,点赞12次,收藏38次。<!doctype html><html><head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="initial-scale=1.0..._js读取csv文件内容

Video.js插件切换视频源并操作m3u8格式视频_video.js 6.2.8-程序员宅基地

文章浏览阅读2.3w次,点赞9次,收藏34次。目录第一章 videojs的配置与简介第二章 更改视频源代码与实例第三章 api的部分应用实例第四章 视频api补充介绍添加事件事件绑定前言:老师让我修改一个需要播放器的界面,之前的播放器有挺多功能需要修改,于是就从网上找了新的播放器插件,也就是Video.js,在网上找了挺久没什么靠谱的资料,于是记录一下。第一章 videojs的配置与简介关于videojs我..._video.js 6.2.8

DataX - 全量数据同步工具_datax工具-程序员宅基地

文章浏览阅读1.1k次,点赞25次,收藏23次。DataX 全量数据同步工具_datax工具

随便推点

python爬虫爬取股票信息(1)-程序员宅基地

文章浏览阅读926次,点赞30次,收藏15次。的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

9、 用户任务(user task)-程序员宅基地

文章浏览阅读2.4k次。用户任务是camunda最常用的任务之一_user task

C51语言检测脉冲个数,利用51单片机统计脉冲个数,即时输出显示-程序员宅基地

文章浏览阅读1.2k次。;利用51单片机设计一个计数显示系统,要求8个数码管显示T1输入脉冲的个数。;最好用汇编语言,加注释,尽量别太复杂。尽量快些哈!;悬赏分:30 | 解决时间:2011-7-20 19:15 |;=====================================================;最佳答案:;题目要求在T1端输入脉冲,统计脉冲个数,并即时输出显示,使用8个LED数码管,显示:0..._c51脉冲计数程序

最长公共子序列(滚动数组优化)_最长公共子序列空间优化-程序员宅基地

文章浏览阅读2.2k次。在网上看过无数的LCS代码,写的非常长,空间时间优化也不咋的,拿来学习非常浪费时间,代码如下。递归公式如下,不理解公共子序列的同学,好好看下面两张图,详情可以参考算法导论。#include using namespace std;int main(){ int i,j,dp[2][10086],t; char a[10086],b[10086]; bo_最长公共子序列空间优化

产品开发必看!一篇讲透,京东商城"双轮驱动"的核心业务模式!-程序员宅基地

文章浏览阅读1.3k次。点击“技术领导力”关注∆每天早上8:30推送京东商城入驻有两大主要模式,一是京东自营的采销模式,另外一个是POP模式,也就是第三方商家入驻模式。1自营京东自营的操作..._双轮驱动业务模式

4月份还有一些hC,还在犹豫的赶紧完善好简历投出去吧,千万不-程序员宅基地

文章浏览阅读828次,点赞17次,收藏23次。本人现在大二,普通二本,在网上看到很多职业规划和找工作的经历,已经开始焦虑了,现在在学校啥也没学,想。无锡人想在无锡找工作,计算机专业,没能力去冲互联网中大厂,so主要准备冲国企,中外合办硕士+雅思7,二维码的大小大概是?年前过了经纬恒润的面试,蔚来汽车二面挂,听从了导师的建议,没有在3月初出去。

推荐文章

热门文章

相关标签