Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
老鱼er
et
提交
bbd1cc98
E
et
项目概览
老鱼er
/
et
与 Fork 源项目一致
从无法访问的项目Fork
通知
10
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
et
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
bbd1cc98
编写于
2月 28, 2019
作者:
T
tanghai
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Book增加数值组件设计一章
上级
1dafd4fc
变更
7
展开全部
隐藏空白更改
内联
并排
Showing
7 changed file
with
1039 addition
and
845 deletion
+1039
-845
Book/2.1CSharp的协程.md
Book/2.1CSharp的协程.md
+1
-1
Book/5.6数值组件设计.md
Book/5.6数值组件设计.md
+214
-0
Unity/Unity.Editor.csproj
Unity/Unity.Editor.csproj
+206
-211
Unity/Unity.Hotfix.csproj
Unity/Unity.Hotfix.csproj
+206
-211
Unity/Unity.Model.csproj
Unity/Unity.Model.csproj
+203
-208
Unity/Unity.ThirdParty.csproj
Unity/Unity.ThirdParty.csproj
+203
-208
Unity/Unity.sln
Unity/Unity.sln
+6
-6
未找到文件。
Book/2.1CSharp的协程.md
浏览文件 @
bbd1cc98
...
...
@@ -70,7 +70,7 @@
}
}
```
我们在这里设计了一个WaitTimeAsync方法,WaitTimeAsync其实就是一个典型的异步方法,它从主线程发起调用,传入了一个WaitTimeFinishCallback回调方法做参数,开启了一个线程,线程Sleep一定时间后,将传过来的回调扔回到主线程执行。OneThreadSynchronizationContext是一个跨线程队列,任何线程可以往里面扔委托,OneThreadSynchronizationContext的Update方法在主线程中调用,会将这些委托取出来放到主线程执行。为什么回调方法需要扔回到主线程执行呢?因为回调方法中读取了loopCount,loopCount在主线程中也有读写,所以要么加锁,要么永远保证只在主线程中读写。加锁是个不好的做法,代码中到处是锁会导致阅读跟维护困难,很容易产生多线程bug。这
中将逻辑打包成委托然后扔回另外一个线程
多线程开发中常用的技巧。
我们在这里设计了一个WaitTimeAsync方法,WaitTimeAsync其实就是一个典型的异步方法,它从主线程发起调用,传入了一个WaitTimeFinishCallback回调方法做参数,开启了一个线程,线程Sleep一定时间后,将传过来的回调扔回到主线程执行。OneThreadSynchronizationContext是一个跨线程队列,任何线程可以往里面扔委托,OneThreadSynchronizationContext的Update方法在主线程中调用,会将这些委托取出来放到主线程执行。为什么回调方法需要扔回到主线程执行呢?因为回调方法中读取了loopCount,loopCount在主线程中也有读写,所以要么加锁,要么永远保证只在主线程中读写。加锁是个不好的做法,代码中到处是锁会导致阅读跟维护困难,很容易产生多线程bug。这
种将逻辑打包成委托然后扔回另外一个线程是
多线程开发中常用的技巧。
我们可能又有个新需求,WaitTimeFinishCallback执行完成之后,再想等3秒,再打印一下loopCount。
```
csharp
...
...
Book/5.6数值组件设计.md
0 → 100644
浏览文件 @
bbd1cc98
类似魔兽世界,moba这种技能极其复杂,灵活性要求极高的技能系统,必须需要一套及其灵活的数值结构来搭配。数值结构设计好了,实现技能系统就会非常简单,否则就是一场灾难。比如魔兽世界,一个人物的数值属性非常之多,移动速度,力量,怒气,能量,集中值,魔法值,血量,最大血量,物理攻击,物理防御,法术攻击,法术防御,等等多达几十种之多。属性跟属性之间又相互影响,buff又会给属性增加绝对值,增加百分比,或者某种buff又会在算完所有的增加值之后再来给你翻个倍。
## 普通的做法:
一般就是写个数值类:
```
c#
class
Numeric
{
public
int
Hp
;
public
int
MaxHp
;
public
int
Speed
;
// 能量
public
int
Energy
;
public
int
MaxEnergy
;
// 魔法
public
int
Mp
;
public
int
MaxMp
;
.....
}
```
仔细一想,我一个盗贼使用的是能量,为什么要有一个Mp的值?我一个法师使用的是魔法为什么要有能量的字段?纠结这个搞毛,当作没看见不就行了吗?实在不行,我来个继承?
```
C#
// 法师数值
calss MageNumeric: Numeric
{
// 魔法
public int Mp;
public int MaxMp;
}
// 盗贼数值
calss RougeNumeric: Numeric
{
// 能量
public int Energy;
public int MaxEnergy;
}
```
10个种族,每个种族7,8种英雄,光这些数值类继承关系,你就得懵逼了吧。面向对象是难以适应这种灵活的复杂的需求的。
再来看看Numeric类,每种数值可不能只设计一个字段,比如说,我有个buff会增加10点Speed,还有种buff增加50%的speed,那我至少还得加三个二级属性字段
```
c#
class
Numeric
{
// 速度最终值
public
int
Speed
;
// 速度初始值
public
int
SpeedInit
;
// 速度增加值
public
int
SpeedAdd
;
// 速度增加百分比值
public
int
SpeedPct
;
}
```
SpeedAdd跟SpeedPct改变后,进行一次计算,就可以算出最终的速度值。buff只需要去修改SpeedAdd跟SpeedPct就行了。
```
c#
Speed
=
(
SpeedInit
+
SpeedAdd
)
*
(
100
+
SpeedPct
)
/
100
```
每种属性都可能有好几种间接影响值,可以想想这个类是多么庞大,初略估计得有100多个字段。麻烦的是计算公式基本一样,但是就是无法统一成一个函数,例如MaxHp,也有buff影响
```
c#
class
Numeric
{
public
int
Speed
;
public
int
SpeedInit
;
public
int
SpeedAdd
;
public
int
SpeedPct
;
public
int
MaxHp
;
public
int
MaxHpInit
;
public
int
MaxHpAdd
;
public
int
MaxHpPct
;
}
```
也得写个Hp的计算公式
```
c#
MaxHp
=(
MaxHpInit
+
MaxHpAdd
)
*
(
100
+
MaxHpPct
)
/
100
```
几十种属性,就要写几十遍,并且每个二级属性改变都要正确调用对应的公式计算. 非常麻烦!
这样设计还有个很大的问题,buff配置表填对应的属性字段不是很好填,例如疾跑buff(增加速度50%),在buff表中怎么配置才能让程序简单的找到并操作SpeedPct字段呢?不好搞。
## ET框架采用了Key Value形式保存数值属性
```
c#
using
System.Collections.Generic
;
namespace
Model
{
public
enum
NumericType
{
Max
=
10000
,
Speed
=
1000
,
SpeedBase
=
Speed
*
10
+
1
,
SpeedAdd
=
Speed
*
10
+
2
,
SpeedPct
=
Speed
*
10
+
3
,
SpeedFinalAdd
=
Speed
*
10
+
4
,
SpeedFinalPct
=
Speed
*
10
+
5
,
Hp
=
1001
,
HpBase
=
Hp
*
10
+
1
,
MaxHp
=
1002
,
MaxHpBase
=
MaxHp
*
10
+
1
,
MaxHpAdd
=
MaxHp
*
10
+
2
,
MaxHpPct
=
MaxHp
*
10
+
3
,
MaxHpFinalAdd
=
MaxHp
*
10
+
4
,
MaxHpFinalPct
=
MaxHp
*
10
+
5
,
}
public
class
NumericComponent
:
Component
{
public
readonly
Dictionary
<
int
,
int
>
NumericDic
=
new
Dictionary
<
int
,
int
>();
public
void
Awake
()
{
// 这里初始化base值
}
public
float
GetAsFloat
(
NumericType
numericType
)
{
return
(
float
)
GetByKey
((
int
)
numericType
)
/
10000
;
}
public
int
GetAsInt
(
NumericType
numericType
)
{
return
GetByKey
((
int
)
numericType
);
}
public
void
Set
(
NumericType
nt
,
float
value
)
{
this
[
nt
]
=
(
int
)
(
value
*
10000
);
}
public
void
Set
(
NumericType
nt
,
int
value
)
{
this
[
nt
]
=
value
;
}
public
int
this
[
NumericType
numericType
]
{
get
{
return
this
.
GetByKey
((
int
)
numericType
);
}
set
{
int
v
=
this
.
GetByKey
((
int
)
numericType
);
if
(
v
==
value
)
{
return
;
}
NumericDic
[(
int
)
numericType
]
=
value
;
Update
(
numericType
);
}
}
private
int
GetByKey
(
int
key
)
{
int
value
=
0
;
this
.
NumericDic
.
TryGetValue
(
key
,
out
value
);
return
value
;
}
public
void
Update
(
NumericType
numericType
)
{
if
(
numericType
>
NumericType
.
Max
)
{
return
;
}
int
final
=
(
int
)
numericType
/
10
;
int
bas
=
final
*
10
+
1
;
int
add
=
final
*
10
+
2
;
int
pct
=
final
*
10
+
3
;
int
finalAdd
=
final
*
10
+
4
;
int
finalPct
=
final
*
10
+
5
;
// 一个数值可能会多种情况影响,比如速度,加个buff可能增加速度绝对值100,也有些buff增加10%速度,所以一个值可以由5个值进行控制其最终结果
// final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;
this
.
NumericDic
[
final
]
=
((
this
.
GetByKey
(
bas
)
+
this
.
GetByKey
(
add
))
*
(
100
+
this
.
GetByKey
(
pct
))
/
100
+
this
.
GetByKey
(
finalAdd
))
*
(
100
+
this
.
GetByKey
(
finalPct
))
/
100
;
Game
.
EventSystem
.
Run
(
EventIdType
.
NumbericChange
,
this
.
Entity
.
Id
,
numericType
,
final
);
}
}
}
```
1.
数值都用key value来保存,key是数值的类型,由NumericType来定义,value都是整数,float型也可以转成整数,例如乘以1000;key value保存属性会变得非常灵活,例如法师没有能量属性,那么初始化法师对象不加能量的key value就好了。盗贼没有法力值,没有法术伤害等等,初始化就不用加这些。
2.
魔兽世界中,一个数值由5个值来影响,可以统一使用一条公式:
```
final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;
```
比如说速度值speed,有个初始值speedbase,有个buff1增加10点绝对速度,那么buff1创建的时候会给speedadd加10,buff1删除的时候给speedadd减10,buff2增加20%的速度,那么buff2创建的时候给speedpct加20,buff2删除的时候给speedpct减20.甚至可能有buff3,会在最终值上再加100%,那么buff3将影响speedfinalpct。这5个值发生改变,统一使用Update函数就可以重新计算对应的属性了。buff配置中对应数值字段相当简单,buff配置中填上相应的NumericType,程序很轻松就能操作对应的数值。
3.
属性的改变可以统一抛出事件给其它模块订阅,写一个属性变化监视器变得非常简单。例如成就模块需要开发一个成就生命值超过1000,会获得长寿大师的成就。那么开发成就模块的人将订阅HP的变化:
```
/// 监视hp数值变化
[NumericWatcher(NumericType.Hp)]
public class NumericWatcher_Hp : INumericWatcher
{
public void Run(long id, int value)
{
if (value > 1000)
{
//获得成就长寿大师成就
}
}
}
```
同理,记录一次金币变化大于10000的异常日志等等都可以这样做。
有了这个数值组件,一个moba技能系统可以说已经完成了一半。
**代码地址:https://github.com/egametang/Egametang**
Unity/Unity.Editor.csproj
浏览文件 @
bbd1cc98
此差异已折叠。
点击以展开。
Unity/Unity.Hotfix.csproj
浏览文件 @
bbd1cc98
此差异已折叠。
点击以展开。
Unity/Unity.Model.csproj
浏览文件 @
bbd1cc98
此差异已折叠。
点击以展开。
Unity/Unity.ThirdParty.csproj
浏览文件 @
bbd1cc98
此差异已折叠。
点击以展开。
Unity/Unity.sln
浏览文件 @
bbd1cc98
...
...
@@ -5,10 +5,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Model", "Unity.Model.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.ThirdParty", "Unity.ThirdParty.csproj", "{E15BADD2-3A26-309A-AB0F-DC5B08044350}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Editor", "Unity.Editor.csproj", "{CD311104-1830-B119-81B6-5DBEE2467FFB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Hotfix", "Unity.Hotfix.csproj", "{1066F652-6A89-D1C4-9881-1A19DF7AB80E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Editor", "Unity.Editor.csproj", "{CD311104-1830-B119-81B6-5DBEE2467FFB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
...
...
@@ -23,14 +23,14 @@ Global
{E15BADD2-3A26-309A-AB0F-DC5B08044350}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E15BADD2-3A26-309A-AB0F-DC5B08044350}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E15BADD2-3A26-309A-AB0F-DC5B08044350}.Release|Any CPU.Build.0 = Release|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Release|Any CPU.Build.0 = Release|Any CPU
{1066F652-6A89-D1C4-9881-1A19DF7AB80E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1066F652-6A89-D1C4-9881-1A19DF7AB80E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1066F652-6A89-D1C4-9881-1A19DF7AB80E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1066F652-6A89-D1C4-9881-1A19DF7AB80E}.Release|Any CPU.Build.0 = Release|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD311104-1830-B119-81B6-5DBEE2467FFB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录