krkr2 中文文档 下载 App

TJS2 类与面向对象编程

为什么要用类

当游戏逻辑变复杂后(比如十几个角色、几十种道具、复杂战斗系统),只用 KAG 标签和零散的全局变量会越来越难维护。类可以帮你:

基本类定义

class Character {
    // 属性(成员变量)
    var name;
    var hp;
    var maxHp;
    var attack;
    
    // 构造函数(类名就是构造函数)
    function Character(name, hp, attack) {
        this.name = name;
        this.hp = hp;
        this.maxHp = hp;
        this.attack = attack;
    }
    
    // 方法
    function takeDamage(damage) {
        this.hp -= damage;
        if (this.hp < 0) this.hp = 0;
        Layer.current.message = this.name + " 受到 " + damage + " 点伤害!";
    }
    
    function isAlive() {
        return this.hp > 0;
    }
}

// 使用
var hero = new Character("阿良良木", 100, 15);
hero.takeDamage(20);

继承

class Hero extends Character {
    var skill;
    var mana;
    
    function Hero(name, hp, attack, skill) {
        super(name, hp, attack);  // 调用父类构造函数
        this.skill = skill;
        this.mana = 50;
    }
    
    // 重写父类方法
    function takeDamage(damage) {
        // Hero 有 20% 几率闪避
        if (Math.random() < 0.2) {
            Layer.current.message = this.name + " 闪避了攻击!";
            return;
        }
        super.takeDamage(damage);
    }
    
    function useSkill() {
        if (this.mana >= 10) {
            this.mana -= 10;
            Layer.current.message = this.name + " 使用了 " + this.skill + "!";
        }
    }
}

class Enemy extends Character {
    var expReward;
    
    function Enemy(name, hp, attack, expReward) {
        super(name, hp, attack);
        this.expReward = expReward;
    }
}

static 静态成员

class GameManager {
    static var instance = null;  // 单例
    
    static function getInstance() {
        if (!GameManager.instance) {
            GameManager.instance = new GameManager();
        }
        return GameManager.instance;
    }
    
    function GameManager() {
        this.totalScore = 0;
    }
}

// 使用单例
var gm = GameManager.getInstance();
gm.totalScore += 100;

接口(Interface)

TJS2 支持 interface,用来规范类的行为契约:

interface Attackable {
    function attack(target);
    function takeDamage(damage);
}

class Character implements Attackable {
    function attack(target) {
        target.takeDamage(this.attack);
    }
    function takeDamage(damage) {
        this.hp -= damage;
    }
}

private/public 成员

TJS2 默认所有成员都是 public。没有内置 private 关键字,但有社区约定:以下划线开头的成员视为私有:

class Player {
    var name;        // 公开
    var _password;   // 约定私有
    
    // 公开的 getter
    function getPassword() {
        // 可以做加密或权限检查
        return this._password;
    }
}

与 KAG scenario 的配合

类的方法可以直接从 scenario 调用:

// 先在 startup.tjs 或 scenario 里创建对象
global hero = new Hero("阿良良木", 100, 15, "流星");

然后 scenario 里:

[eval exp="hero.name"] 准备战斗!

[tjs] hero.takeDamage(15);

[if exp="hero.isAlive()"]
英雄还活着!
[else]
英雄倒下了……
[endif]

项目实战建议

什么时候该用类

场景建议
纯文本 AVG,没有复杂系统只用 KAG 标签 + 全局变量就够
有多角色、多状态需要管理用 Character 类封装
有战斗、合成等游戏系统用多个类协作
需要 UI 界面(按钮、选项)自定义类继承 EngineWidget
想让 KAG 某个标签有特殊行为修改或继承 KAG 的 TJS2 源码

常见模式