0%

所有那些太美的、太好的、太深刻的、太慎重的、太重大的东西,总让人下意识地想去躲避。

佚名

Lua

介绍


Lua 是一种轻量小巧的脚本语言,常与c++/c配合进行游戏的开发。对于一些需要动态实现的部分,可以说十分胜任。

下载

编译源码

  • 将源码包src下的源文件导入到vs工程下
  • 注意需要去除【lua.c】【luac.c】两个文件,不然无法编译通过

编写测试

  • test.lua
  • 在lua中定义内容,在c++中获取
str = "I am so cool"
tbl = {name = "shun", id = 20114442}
function add(a,b)
return a + b
end
  • main.cpp
  • 创建可运行的lua虚拟栈
#include <iostream>
#include <string.h-
#include <cassert>
#include <lua.hpp>

using namespace std;

int main()
{
//1.创建Lua状态
lua_State* L = luaL_newstate();
if (L == NULL)
{
cout << "state is null\n";
return 0;
}
//2.加载lua文件
int bRet = luaL_loadfile(L, "test.lua");
if (bRet) {
cout << "load file error" << endl;
return 0;
}
//3.运行Lua文件
bRet = lua_pcall(L, 0, 0, 0);
if (bRet) {
cout << "pcall error" << endl;
return 0;
}
//4.读取变量
lua_getglobal(L, "str");
string str = lua_tostring(L, -1);
cout << "str = " << str.c_str() << endl;
//5.读取table
lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");
str = lua_tostring(L, -1);
cout << "tbl:name = " << str.c_str() << endl;
//6.读取函数
lua_getglobal(L, "add"); // 获取函数,压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
int iRet = lua_pcall(L, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。
if (iRet) // 调用出错
{
const char* pErrorMsg = lua_tostring(L, -1);
cout << pErrorMsg << endl;
lua_close(L);
return 0;
}
if (lua_isnumber(L, -1)) //取值输出
{
double fValue = lua_tonumber(L, -1);
cout << "Result is " << fValue << endl;
}
//7关闭state
lua_close(L);
return 0;
}

执行结果

  • lua文件内定义变量和函数被顺利取出
  • 当前已经具备了执行lua的环境
  • 但是这样的操作台过麻烦,每次倒要去做出栈和压栈操作,还要知道数据栈的位置

SOL

介绍


当前的方式使用lua还是比较的麻烦,我们需要借助于一个框架实现lua与c++的绑定。

下载

打包单一头文件

  • 运行源码 single文件夹下的single.py脚本
  • 可以得到单一的头文件
  • 导入项目即可

C++与Lua

编写一个主入口类

  • GameMain.h
  • 作为当前主程序的入口,用于初始化lua运行时和绑定
  • printNum为需要在lua中调用的函数
#pragma once
#include <iostream>

namespace LuaScripting
{
class LuaRuntime;
}

using namespace std;

class GameMain
{
private:
LuaScripting::LuaRuntime* m_luaRuntime;
public:
void init();
bool printNum(int num);
LuaScripting::LuaRuntime* getLuaRuntime();
};
  • GameMain.cpp
#include "GameMain.h"
#include "LuaRuntime.h"

void GameMain::init()
{
m_luaRuntime = new LuaScripting::LuaRuntime();
m_luaRuntime->init(this);
}

bool GameMain::printNum(int num)
{
cout << "num:" << num << endl;
return false;
}

LuaScripting::LuaRuntime* GameMain::getLuaRuntime()
{
return m_luaRuntime;
}

编写一个lua运行时类

  • LuaRuntime.h
#pragma once
#include <sol/sol.hpp>
class GameMain;

namespace LuaScripting
{
class LuaRuntime
{
public:
LuaRuntime();
~LuaRuntime();
bool init(GameMain* main);
void cleanup();
sol::state* getLuaState();
private:
sol::state* m_state;
};

}
  • LuaRuntime.cpp
  • 在init的时候初始化sol
    • 打开需要用到的标准库
    • 注册绑定C++函数
    • 注册全局变量
  • 在初始化和启动的时候调用对应的lua文件
#include "LuaRuntime.h"
#include "LuaAPI.h"
#include "GameMain.h"

namespace LuaScripting
{

LuaRuntime::LuaRuntime()
{
m_state = NULL;
}

LuaRuntime::~LuaRuntime()
{
delete m_state;
}

bool LuaRuntime::init(GameMain* main)
{
m_state = new sol::state();

// 打开标准库
m_state->open_libraries(
sol::lib::base,
sol::lib::package,
sol::lib::coroutine,
sol::lib::string,
sol::lib::table,
sol::lib::math,
sol::lib::io,
sol::lib::debug,
sol::lib::os
);

RegisterUserTypes(m_state);
RegisterGlobals(m_state, main);

// 执行Lua初始化脚本,注册枚举、全局工具函数等
const auto result = m_state->safe_script("require \"bootstrap\"",
sol::script_pass_on_error);
if (!result.valid())
{
const sol::error err = result;
return false;
}

return false;
}

void LuaRuntime::cleanup()
{
const auto result = m_state->safe_script("require \"shutdown\"",
sol::script_pass_on_error);
if (!result.valid())
{
const sol::error err = result;
}
}

sol::state* LuaRuntime::getLuaState()
{
return m_state;
}
}

编写luaAPI

  • LuaAPI.h
  • 定义命名空间用来包含需要在lua中实现调用的内容
#pragma once
#include <sol/sol.hpp>

class GameMain;

namespace LuaScripting
{
class NativeUtils {
private:
sol::state* m_state;
GameMain* m_main;
public:
NativeUtils(sol::state* state, GameMain* main);

GameMain* GetMain();
};

void RegisterUserTypes(sol::state* state);

void RegisterGlobals(sol::state* state, GameMain* main);
}
  • LuaAPI.cpp
#include "LuaAPI.h"
#include "GameMain.h"

namespace LuaScripting
{
void RegisterUserTypes(sol::state* state) {

const auto printNum = [](GameMain& main, int num) {
main.printNum(num);
return true;
};
state->new_usertype<GameMain>("GameMain",
"new", sol::no_constructor,
"printNum", printNum
);

state->new_usertype<NativeUtils>("NativeUtils",
"new", sol::no_constructor,
"GetMain", &NativeUtils::GetMain
);
}

void RegisterGlobals(sol::state* state, GameMain* main)
{
(*state)["g_nativeUtils"] = NativeUtils{ state, main };
}

NativeUtils::NativeUtils(sol::state* state, GameMain* main)
: m_state(state)
, m_main(main)
{

}

GameMain* NativeUtils::GetMain()
{
return m_main;
}
}

编写入口

  • main.cpp
#include <iostream>
#include <string.h-
#include <cassert>
#include <sol.hpp>
#include "GameMain.h"
#include "LuaRuntime.h"

using namespace std;

int main() {
GameMain* main = new GameMain();
main->init();
main->getLuaRuntime()->getLuaState()->script_file("hello.lua");

return 0;
}

一些lua文件

  • bootstrap.lua
  • 启动回调文件
print("-------------bootstrap----------------")
  • shutdown.lua
  • 关闭回调文件
print("--------------shutdown---------------")
  • hello.lua
  • 执行lua的输出
  • 调用了GameMain中定义的方法
print("Hello CPP")
print(g_nativeUtils)
g_main = g_nativeUtils:GetMain()
print(g_main)
g_main:printNum(10086)

执行测试

  • c++与lua实现了互相调用
  • C++与lua实现了函数绑定

TIPS

  • lua虚拟栈
  • lua虚拟栈的流程图
  • SOL对C++的新特性支持比较激进,需要使用最新的C++版本编译
  • SOL中还有许多内容,比如线程 优化等
    https://sol2.readthedocs.io/en/latest/

Html加载js脚本

常规加载js文件

  • js文件的加载会堵塞当前的html文档解析,只有当js脚本加载完成并执行后才继续接下来的文档解析
<script src = "xx.js"></script>

async方式加载js文件

  • js文件的加载分为下载和执行两个部分,下载js文件不会阻塞文档解析,下载完成之后当即执行js文件(无法明确文件的执行顺序)
<script src = "xx.js" async></script>

defer方式加载js文件

  • js文件的加载分为下载和执行两个部分,下载js文件不会阻塞文档解析,当下载完并且文档解析完成的时候执行js文件(有明确文件的执行顺序))
<script src = "xx.js" defer></script>

不同方式的加载顺序图

测试demo

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

</head>
<body>
<script src = "./defer2.js" defer></script>
<script src = "./defer1.js" defer></script>
<script src = "./async1.js" async></script>
<script src = "./async2.js" async></script>
<script src = "./normal.js"></script>
<div id='view' style="width:200px;height:200px;"></div>
</body>
</html>
  • async1.js
console.log('----------async1.js开始执行------------')
  • async2.js
console.log('----------async2.js开始执行------------')
  • defer1.js
console.log('----------defer1.js开始执行------------')
let $view = document.getElementById('view');
$view.style.backgroundColor = '#FF0000';
  • defer2.js
console.log('----------defer2.js开始执行-----------')
  • normal.js
window.onload = function () {
console.log('----------文档加载完成----------')
}
console.log('----------常规js执行------------')
  • 测试结果
----------async1.js开始执行------------
----------常规js执行------------
----------defer2.js开始执行-----------
----------defer1.js开始执行-----------
----------async2.js开始执行------------

使用Go

创建main.go文件

  • main.go
package main

import ("fmt")

func main() {
fmt.Println("Hello, Go WebAssembly!")
}

编译go文件,得到lib.wasm文件

set GOOS=js
echo %GOOS%
set GOARCH=wasm
echo %GOARCH%
go build -o lib.wasm main.go
pause

获取wasm_exec.js

https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js

新建一个index.html文件

  • index.html
<!doctype html>
<!--
Copyright 2018 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<html>

<head>
<meta charset="utf-8">
<title>Go wasm</title>
</head>

<body>
<!--
Add the following polyfill for Microsoft Edge 17/18 support:
<script src="https://cdn.jsdelivr.net/npm/text-encoding@0.7.0/lib/encoding.min.js"></script>
(see https://caniuse.com/#feat=textencoder)
-->
<script src="wasm_exec.js"></script>
<script>
if (!WebAssembly.instantiateStreaming) { // polyfill
WebAssembly.instantiateStreaming = async (resp, importObject) =- {
const source = await (await resp).arrayBuffer();
return await WebAssembly.instantiate(source, importObject);
};
}

const go = new Go();
console.log(go.importObject);
console.log(WebAssembly);
go.importObject.env = go.importObject.env || {};
go.importObject.env['command-line-arguments.add'] = function(x, y) {
return x + y
};

let mod, inst;
WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then((result) =- {
mod = result.module;
inst = result.instance;
document.getElementById("runButton").disabled = false;
}).catch((err) =- {
console.error(err);
});

async function run() {
console.clear();
await go.run(inst);
inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
}

</script>

<button onClick="run();" id="runButton" disabled>Run</button>
</body>

</html>

需要go服务器支持wasm的meta类型

  • server.go
package main

import (
"flag"
"log"
"net/http"
)

var (
listen = flag.String("listen", ":8080", "listen address")
dir = flag.String("dir", ".", "directory to serve")
)

func main() {
flag.Parse()
log.Printf("listening on %q...", *listen)
err := http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir)))
log.Fatalln(err)
}

启动服务器

go run server.go

使用Tinygo

创建main.go文件

  • main.go
package main

func main() {
println("Hello, tinygo wsam!!!")
println("adding two numbers:", add(2, 3)) // expecting 5
}

func add(x, y int) int

func multiply(x, y int) int {
return x * y;
}

编译go文件,得到lib.wasm文件

tinygo build -o lib.wasm -target wasm ./main.go
pause

获取wasm_exec.js

https://github.com/tinygo-org/tinygo/blob/release/targets/wasm_exec.js

新建一个index.html文件

  • index.html
<!doctype html>
<!--
Copyright 2018 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<html>

<head>
<meta charset="utf-8">
<title>Go wasm</title>
</head>

<body>
<!--
Add the following polyfill for Microsoft Edge 17/18 support:
<script src="https://cdn.jsdelivr.net/npm/text-encoding@0.7.0/lib/encoding.min.js"></script>
(see https://caniuse.com/#feat=textencoder)
-->
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
const WASM_URL = 'lib.wasm';
go.importObject.env = go.importObject.env || {};
go.importObject.env['command-line-arguments.add'] = function(x, y) {
return x + y
};
console.log(go.importObject);
var wasm;
if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
wasm = obj.instance;
go.run(wasm);
console.log('multiplied two numbers:', wasm.exports.multiply(5, 3));
})
} else {
fetch(WASM_URL).then(resp =>
resp.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
wasm = obj.instance;
go.run(wasm);
console.log('multiplied two numbers:', wasm.exports.multiply(5, 3));
})
)
}

</script>
</body>

</html>

需要go服务器支持wasm的meta类型

  • server.go
package main

import (
"flag"
"log"
"net/http"
)

var (
listen = flag.String("listen", ":8080", "listen address")
dir = flag.String("dir", ".", "directory to serve")
)

func main() {
flag.Parse()
log.Printf("listening on %q...", *listen)
err := http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir)))
log.Fatalln(err)
}

启动服务器

go run server.go

json内容

[
"do",
[
"def",
"drawTriangle",
[
"fn",
["left", "top", "right", "color"],
[
"do",
["drawLine", "left", "top", "color"],
["drawLine", "top", "right", "color"],
["drawLine", "left", "right", "color"]
]
]
],
["consoleLine"],
["drawTriangle", {"x": 0, "y": 0}, {"x": 3, "y": 3}, {"x": 6, "y": 0}, "蓝色"],
["consoleLine"],
["drawTriangle", {"x": 6, "y": 6}, {"x": 10, "y": 10}, {"x": 6, "y": 16}, "红色"],
["consoleLine"]
]

解析文件

let instructions = require("./instructions.json");

function drawLine(p1, p2, color) {
console.log(`画出当前一条线[${p1.x}, ${p1.y}]到[${p2.x}, ${p2.y}],颜色为${color}`);
return {p1, p2};
}

function rotate(node, angle) {
console.log(`${JSON.stringify(node)}旋转角度:${angle}`);
return {angle};
}

function consoleLine() {
console.log("-----------------------------");
}

function parseRPC(jsonStr) {
// 当前变量存储的地方
const variables = {};
// 当前支持的函数集合
const fns = {
drawLine,
rotate,
consoleLine,
do: (...args) => args[args.length - 1],
def: (name, v) => variables[name] = v,
}

// 将数组转化为map
const mapArgsWithValues = (args, values) => {
return args.reduce((res, key, idx) => {
res[key] = values[idx];
return res;
}, {});
}

// 解析函数
const parseFnInstruction = (args, body, oldVariables) => {
return (...values) => {
const newVariables = {
...oldVariables,
...mapArgsWithValues(args, values),
};
return parseInstruction(body, newVariables);
}
}

// 解析指令
const parseInstruction = (ins, variables) => {
// 判断是否是已经定义的变量
if (variables[ins]) {
return variables[ins];
}
// 判断是否是数组
if (!Array.isArray(ins)) {
return ins;
}
const [fName, ...args] = ins;
// 当前的内容为fn的时候构建新的函数
if (fName === "fn") {
return parseFnInstruction(...args, variables);
}
// 先检索定义的函数表 没有的话就从自定义表找
const fn = fns[fName] || variables[fName];
return fn(...args.map(arg => parseInstruction(arg, variables)));
}
// 解析
parseInstruction(jsonStr, variables);
}

parseRPC(instructions);

调用结果

-----------------------------
画出当前一条线[0, 0]到[3, 3],颜色为蓝色
画出当前一条线[3, 3]到[6, 0],颜色为蓝色
画出当前一条线[0, 0]到[6, 0],颜色为蓝色
-----------------------------
画出当前一条线[6, 6]到[10, 10],颜色为红色
画出当前一条线[10, 10]到[6, 16],颜色为红色
画出当前一条线[6, 6]到[6, 16],颜色为红色
-----------------------------

Tips

可以用于游戏中一些带函数调用的配置文件处理,比如关卡的配置和人物技能配置等

参考链接

https://stopa.io/post/265

属性因子的类型

  • 可以增加百分比值
  • 可以增加固定值
/**
* 属性值的因数类型
*
* @enum {number}
*/
enum AttrFactorType {
AFT_Base, // 基础值
AFT_Add_Constant, // 增加固定值
AFT_Add_Percentile, // 增加百分比
AFT_Max // 最大值
};

属性元素

  • 区分属性因子
  • 用于控制属性元素的类
/**
* 属性元素
*
* @class UnitAttrElement
*/
class UnitAttrElement {
private m_aiValues: Array<number> = []; // 属性数组
private m_iResult: number = 0; // 结果
constructor() {
this.clear();
}

/**
* 清除
*
* @memberof SeUnitAttrElem
*/
clear() {
this.m_aiValues[AttrFactorType.AFT_Base] = 0;
this.m_aiValues[AttrFactorType.AFT_Add_Constant] = 0;
this.m_aiValues[AttrFactorType.AFT_Add_Percentile] = 0;
this.m_iResult = 0;
}

/**
* 元素
*
* @param {AttrFactorType} eType
* @memberof SeUnitAttrElem
*/
getFactor(eType: AttrFactorType) {
return this.m_aiValues[eType];
}

/**
* 设置元素
*
* @param {AttrFactorType} eType
* @param {number} val
* @memberof SeUnitAttrElem
*/
setFactor(eType: AttrFactorType, val: number) {
this.m_aiValues[eType] = val;
}

/**
* 设置结果
*
* @memberof SeUnitAttrElem
*/
set result(val: number) {
this.m_iResult = val;
}

/**
* 获取结果
*
* @memberof SeUnitAttrElem
*/
get result() {
return this.m_iResult;
}
}

单位属性值的类型

  • 单位拥有的属性
/**
* 单位属性类型
*
* @enum {number}
*/
enum UnitAttrType {
UAT_ATK, // 攻击力
UAT_ATK_INTERVAL, // 攻击间隔
UAT_DEF, // 防御力
UAT_HP, // 当前生命值
UAT_HP_LIMIT, // 生命值上限
UAT_MP, // 当前的魔法值
UAT_MP_LIMIT, // 魔法值上限
UAT_SPEED, // 速度
UAT_DODGE, // 闪避
UAT_MAX,
}

单位属性类

  • 可以挂在单位类上,用于计算属性
/**
* 单位属性类
*
* @class UnitAttr
*/
class UnitAttr {
private m_akUnitAttr: Array<UnitAttrElement> = []; // 属性数组
static SCALE = 1; // 缩放数据用于去除小数 转化为定点数
static PERCENTAGE_100 = 100 * UnitAttr.SCALE; // 百分百
static MIN_ATTACK_VALUE = 1 * UnitAttr.SCALE; // 攻击力最小值为1
static MIN_HP_LIMIT = 1 * UnitAttr.SCALE; // 最小生命值
static MIN_MP_LIMIT = 1 * UnitAttr.SCALE; // 最小魔法值
static MIN_ATK_INTERVAL_PERCENTAGE = -50 * UnitAttr.SCALE; // 最小攻击间隔
static MIN_ATK_INTERVAL_VALUE = 150 * UnitAttr.SCALE; // 最小攻击间隔值
static MAX_DODGE_CHANCE = 95 * UnitAttr.SCALE; // 最大闪避百分比
static MIN_MOVE_SPEED = 15 * UnitAttr.SCALE; // 最小移动速度

constructor() {
// 初始化当前的属性数组
for (let i = 0; i < UnitAttrType.UAT_MAX; i++) {
this.m_akUnitAttr[i] = new UnitAttrElement();
}
}

/**
* 根据配置文件的内容设置当前单位属性
*
* @param {*} res
* @memberof UnitAttr
*/
init(res: any) {
// 初始化单位的相关基础属性
}

/**
* 清理属性
*
* @param {UnitAttrType} eType
* @memberof UnitAttr
*/
clear(eType: UnitAttrType) {
this.m_akUnitAttr[eType].clear();
}

/**
* 重置当前属性因子
*
* @param {UnitAttrType} eAttrType
* @param {AttrFactorType} eFactorType
* @param {number} iValue
* @memberof UnitAttr
*/
resetCommonAttrFactor(eAttrType: UnitAttrType, eFactorType: AttrFactorType, iValue: number) {
this.m_akUnitAttr[eAttrType].setFactor(eFactorType, iValue);
this.updateCascade(eAttrType);
}

/**
* 修改属性因子
*
* @param {UnitAttrType} eAttrType
* @param {AttrFactorType} eFactorType
* @param {number} iValue
* @memberof UnitAttr
*/
incrementCommonAttrFactor(eAttrType: UnitAttrType, eFactorType: AttrFactorType, iValue: number) {
let old = this.m_akUnitAttr[eAttrType].getFactor(eFactorType);
this.m_akUnitAttr[eAttrType].setFactor(eFactorType, old + iValue);
// 立即更新本属性以及它的相关属性,不等到Update
this.updateCascade(eAttrType);
}

/**
* 读取当前属性结果
*
* @param {UnitAttrType} eType
* @returns
* @memberof UnitAttr
*/
readCommonAttrResult(eType: UnitAttrType) {
return this.m_akUnitAttr[eType].result;
}

/**
* 获取因子
*
* @param {UnitAttrType} eAttrType
* @param {AttrFactorType} eFactorType
* @returns
* @memberof UnitAttr
*/
readCommonAttrFactor(eAttrType: UnitAttrType, eFactorType: AttrFactorType) {
return this.m_akUnitAttr[eAttrType].getFactor(eFactorType);
}

/**
* 更新需要修改的主属性并修改相关的属性
* 每一点防御力增加一点血量之类的
* @param {UnitAttrType} ePrimaryAttr
* @memberof UnitAttr
*/
updateCascade(ePrimaryAttr: UnitAttrType) {
// 先更新主属性
this.updateSingle(ePrimaryAttr);
}

/**
* 刷新所有属性值
*
* @memberof UnitAttr
*/
updateAllAttrs() {
for (let i = 0; i < UnitAttrType.UAT_MAX; i++) {
this.updateCascade(i);
}
}

/**
* 只更新当前自己的主属性
*
* @param {UnitAttrType} eType
* @memberof UnitAttr
*/
private updateSingle(eType: UnitAttrType) {
// 获取属性元素
let attrElem = this.m_akUnitAttr[eType];
switch(eType) {
case UnitAttrType.UAT_ATK: {
attrElem.result = this.updateATK(attrElem);
break;
}
case UnitAttrType.UAT_ATK_INTERVAL: {
attrElem.result = this.updateATK_INTERVAL(attrElem);
break;
}
case UnitAttrType.UAT_DEF: {
attrElem.result = this.updateDEF(attrElem);
break;
}
case UnitAttrType.UAT_HP: {
attrElem.result = this.updateHP(attrElem);
break;
}
case UnitAttrType.UAT_HP_LIMIT: {
attrElem.result = this.updateHP_LIMIT(attrElem);
break;
}
case UnitAttrType.UAT_MP: {
attrElem.result = this.updateMP(attrElem);
break;
}
case UnitAttrType.UAT_MP_LIMIT: {
attrElem.result = this.updateMP_LIMIT(attrElem);
break;
}
case UnitAttrType.UAT_SPEED: {
attrElem.result = this.updateSPEED(attrElem);
break;
}
case UnitAttrType.UAT_DODGE: {
attrElem.result = this.updateDODGE(attrElem);
break;
}
}
}

/**
* 更新攻击力
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateATK(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < UnitAttr.MIN_ATTACK_VALUE) {
iResult = UnitAttr.MIN_ATTACK_VALUE;
}
return iResult;
}

/**
* 更新攻击间隔
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateATK_INTERVAL(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
if (iAddPer < UnitAttr.MIN_ATK_INTERVAL_PERCENTAGE) {
iAddPer = UnitAttr.MIN_ATK_INTERVAL_PERCENTAGE;
}
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < UnitAttr.MIN_ATK_INTERVAL_VALUE) {
iResult = UnitAttr.MIN_ATK_INTERVAL_VALUE;
}
return iResult;
}

/**
* 更新防御力
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateDEF(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
return iResult;
}

/**
* 更新当前生命值
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateHP(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < 0) {
iResult = 0;
}
return iResult;
}

/**
* 更新最大生命值
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateHP_LIMIT(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < UnitAttr.MIN_HP_LIMIT) {
iResult = UnitAttr.MIN_HP_LIMIT;
}
return iResult;
}

/**
* 更新当前魔法值
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateMP(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < 0) {
iResult = 0;
}
return iResult;
}

/**
* 更新最大魔法值
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateMP_LIMIT(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < UnitAttr.MIN_MP_LIMIT) {
iResult = UnitAttr.MIN_MP_LIMIT;
}
return iResult;
}

/**
* 更新速度
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateSPEED(rkAttr: UnitAttrElement) : number {
let iBase = rkAttr.getFactor(AttrFactorType.AFT_Base);
let iAddValue = rkAttr.getFactor(AttrFactorType.AFT_Add_Constant);
let iAddPer = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
let iResult = (iBase + iAddValue) * (1 + iAddPer / UnitAttr.PERCENTAGE_100);
if (iResult < UnitAttr.MIN_MOVE_SPEED) {
iResult = UnitAttr.MIN_MOVE_SPEED;
}
return iResult;
}

/**
* 更新闪避几率
*
* @param {UnitAttrElement} rkAttr
* @memberof UnitAttr
*/
private updateDODGE(rkAttr: UnitAttrElement) : number {
let iResult = rkAttr.getFactor(AttrFactorType.AFT_Add_Percentile);
if (iResult < 0) {
iResult = 0;
}
if (iResult > UnitAttr.MAX_DODGE_CHANCE) {
iResult = UnitAttr.MAX_DODGE_CHANCE;
}
return iResult;
}
}