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 源码 |
常见模式
- 单例:GameManager、AudioManager 全局唯一
- 工厂模式:CharacterFactory.createHero()、CharacterFactory.createEnemy()
- 状态机:角色状态(正常、受伤、死亡)用状态类管理
- 事件系统:自定义事件回调(比如存档时通知所有 Manager 保存自己的数据)