暗黑战神

思考并回答以下问题:

  • Delegate和delegate的区别是什么?public abstract Delegate Action {get;set;}是什么意思?
  • internal和public的区别是什么?哪个范围更大?
  • IEnumerator里面基本都是while(true)。怎么理解?
  • 每个a.json文件都对应一个a.cs实体数据文件。暗黑战神每个xml对应一个数据实体cs文件,都是GameData的子类。然后还有一个GameDataController类来管理所有这些GameData的子类。怎么理解?
  • 静态构造函数主要目的是用于初始化一些静态的变量。怎么理解?
  • 基本没有脚本是预先挂载的,都是动态挂载。
  • 扩展方法第一个参数前缀为this,表示需要扩展类对象,从第二个参数开始,为扩展方法参数列表。怎么理解?
  • default(T)是什么意思?为什么要这样写?if(default(T) == null)是判断什么的?

资源加载

Unity3d常用的两种加载资源方案:Resources.Load和AssetBundle。

1、Resources.Load:使用这种方式加载资源,首先需要在Asset目录下创建一个名为Resources的文件夹,这个命名是U3D规定的方式,然后把资源文件放进去,当然也可以在Resources中再创建子文件夹,当然在代码加载时需要添加相应的资源路径。

2、项目中更常用的是使用AssetBundle的方式动态加载游戏对象。
使用AssetBundle打包预设或者场景可以将与其相关的所有资源打包,这样很好地解决资源的依赖问题,使得我们可以方便的加载GameObject。

\client\Assets\Scripts\ClientCore\Resource.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Collections;
using Mogo.Util;

public class Resource
{
internal AssetBundleCreateRequest createRequest;
internal WWW www;
internal List<Resource> dependencies;
internal Byte[] fileData;
public int referenceCount;
internal UnityEngine.Object m_object;
internal bool m_isDone;

internal float m_progress
{
get
{
if (www != null)
return www.progress;
else
return 0;
}
}

public string RelativePath { get; internal set; }

public bool IsLoading { get; internal set;}
}

client\Assets\Scripts\ClientCore\ResourceManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Eddy;
using Eddy.Extensions;
using UnityEngine;
using System.Security;
namespace Mogo.Util
{
// 静态类
public static class ResourceManager
{
public static readonly string MetaFileName = "Meta.xml";
public static Dictionary<string, Resource> resources = new Dictionary<string, Resource>();

public static List<string> GetResourceRoots(string resourceName)
{
var result = new List<string>();
foreach (var item in resources)
{
if (item.Value.dependencies != null && item.Value.dependencies.FirstOrDefault(t => Utils.GetFileNameWithoutExtention(t.RelativePath) == resourceName) != null)
result.Add(item.Key);
}
return result;
}
}
}

游戏初始化

最开始的是InitScene,只有这个场景在Scenes文件下,其他都在Resources文件夹下。这个场景只有一个Driver空对象,带有Initializer脚本。这个脚本放在Plugins文件夹下。

\client\Assets\Plugins\Init\Initializer.cs

1
2
3
4
5
6
7
8
9
10
using UnityEngine;

public class Initializer : MonoBehaviour
{
void Start ()
{
gameObject.AddComponent<Driver>();
DestroyImmediate(this);
}
}

\client\MogoSolution\LoaderLib\Init\PluginCallback.cs

1
2


\client\Assets\Plugins\Init\Driver.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#if !UNITY_IPHONE

using UnityEngine;

using Mogo.Util;
using System;
using System.Collections;
using System.Net;
using System.IO;

public class Driver : MonoBehaviour
{
private static bool bLoodLib = false;
public static String FileName
{
get
{
return "MogoRes";
}
}

// C#的DES只支持64bits的Key
// http://msdn.microsoft.com/en-us/library/system.security.cryptography.des.key(VS.80).aspx
public static byte[] Number
{
get
{
return Utils.GetResNumber();
}
}

public static bool IsRunOnAndroid = false;
public Action LevelWasLoaded;
public static Driver Instance;

void Awake()
{
#if !UNITY_EDITOR
LoggerHelper.CurrentLogLevels = LogLevel.Info | LogLevel.ERROR | LogLevel.CRITICAL | LogLevel.EXCEPT; // | LogLevel.WARNING
#endif
LoggerHelper.Info("Game Start!");
SystemSwitch.InitSystemSwitch();
// 防止设备休眠
Screen.sleepTimeout = (int)SleepTimeout.NeverSleep;
DontDestroyOnLoad(transform.gameObject);
Instance = this;
gameObject.AddComponent<DriverLib>();
Application.targetFrameRate = 30;
DefaultUI.InitLanguageInfo();
GameObject.Find("MogoForwardLoadingUIPanel").AddComponent<MogoForwardLoadingUIPanel>();
TryToInit();
// 1秒后调用Tick方法,之后每隔0.02秒调用一次
InvokeRepeating("Tick", 1, 0.02f);
gameObject.AddComponent<AudioListener>()

}
}

配置文件

XML

暗黑战神\client\Assets\Resources\data\xml\FXData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<root>
<record>
<id>910101</id>
<actionID>8</actionID>
<slot>Bip_master</slot>
<duration>1.5</duration>
<isConflict>1</isConflict>
<isStatic>0</isStatic>
<level>0</level>
<group>0</group>
<locationType>1</locationType>
<location>0.0, 0.0, 0.0</location>
<player>Avatar_101_blade</player>
<resourcePath>H_blade_skill_1_1.prefab</resourcePath>
</record>

<record>
<id>910102</id>
<actionID>8</actionID>
<slot>Bip_master</slot>
<duration>1.5</duration>
<isConflict>1</isConflict>
<isStatic>0</isStatic>
<level>0</level>
<group>0</group>
<locationType>3</locationType>
<location>0.0, 0.0, 0.0</location>
<player>Avatar_101_blade</player>
<resourcePath>H_blade_skill_1_2.prefab</resourcePath>
<delay>0.23</delay>
</record>
</root>

MogoSolution\GameData\GameData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:GameData
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:配置数据抽象类。
//----------------------------------------------------------------*/
#endregion

using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using Mogo.Util;
using System.Diagnostics;
using HMF;

namespace Mogo.GameData
{
public abstract class GameData
{
public int id { get; protected set; }

protected static Dictionary<int, T> GetDataMap<T>()
{
Dictionary<int, T> dataMap;
// Stopwatch一般用来测量代码执行所用的时间或者计算性能数据,在优化代码性能上可以使用Stopwatch来测量时间。
Stopwatch sw = new Stopwatch();
sw.Start();
var type = typeof(T);
// 继承GameData的子类有这个字段
var fileNameField = type.GetField("fileName"); // 返回FieldInfo类型
if (fileNameField != null)
{
var fileName = fileNameField.GetValue(null) as String; // 如果该字段为静态,obj则忽略。对于非静态字段,obj应为继承或声明该字段的类的实例。
var result = GameDataControler.Instance.FormatData(fileName, typeof(Dictionary<int, T>), type); // 返回实例对象
// (xml/FXData, Type类实例(Dictionary), Type类实例(FXData))
dataMap = result as Dictionary<int, T>;
}
else
{
dataMap = new Dictionary<int, T>();
}
sw.Stop();
LoggerHelper.Info(String.Concat(type, " time: ", sw.ElapsedMilliseconds));
return dataMap;
}
}

public abstract class GameData<T> : GameData where T : GameData<T>
{
private static Dictionary<int, T> m_dataMap;

public static Dictionary<int, T> dataMap
{
get
{
if (m_dataMap == null)
m_dataMap = GetDataMap<T>();
return m_dataMap;
}
set { m_dataMap = value; }
}
}

public class GameDataControler : DataLoader
{
private List<Type> m_defaultData = new List<Type>()
{
typeof(GlobalData), typeof(MapData), typeof(LanguageData), typeof(UIMapData),typeof(SoundData), typeof(InstanceLevelGridPosData),typeof(MapUIMappingData)
}

private static GameDataControler m_instance;

public state GameDataControler Instance
{
get
{
return m_instance;
}
}
// 静态构造函数是在构造函数方法前面添加了static关键字之后形成的,并且没有修饰符(public,private),没有参数。
// 若一个类中有静态构造函数,在首次实例化该类或任何的静态成员被引用时,.NET自动调用静态构造函数来初始化该类。注意是“首次”,即继续实例化该类时,不会调用该类的静态构造函数。
// 主要目的是用于初始化一些静态的变量。
// 如果我们在类中定义了静态变量,但是又没有定义静态构造函数,那么框架也会帮助我们来生成一个静态构造函数来让框架自身来调用。
static GameDataControler()
{
m_instance = new GameDataControler();
}

public object FormatData(string fileName, Type dicType, Type type)
{
if (SystemSwitch.UseHmf)
return FormatHmfData(String.Concat(m_resourcePath, fileName, m_fileExtention), dicType, type);
else
return FormatXMLData(String.Concat(m_resourcePath, fileName, m_fileExtention), dicType, type);
}

#region xml

/// <summary>
/// 返回Dictionary<int, FXData>字典
/// </summary>
/// <param name="fileName">文件路径与文件名</param>
/// <param name="dicType">字典Type</param>
/// <param name="type">子类Type</param>
/// <returns></returns>
private object FormatXMLData(string fileName, Type dicType, Type type)
{
object result = null;
try
{
//LoggerHelper.Debug(fileName);
//var dicType = dicProp.PropertyType;
// Type.EmptyTypes表示Type类型的空数组。此字段为只读。
// 相当于new一个字典类型
result = dicType.GetConstructor(Type.EmptyTypes).Invoke(null);
Dictionary<Int32, Dictionary<String, String>> map;// int32为id,string为属性名,string为属性值

// 以FXData举例,实例化FXData对象并根据xml给其属性赋值
if (XMLParser.LoadIntMap(fileName, m_isUseOutterConfig, out map))
{
// 返回PropertyInfo[]表示当前PropertyInfo的所有公共属性的Type对象数组。
var props = type.GetProperties();// 获取实体属性
foreach (var item in map)
{
var t = type.GetConstructor(Type.EmptyTypes).Invoke(null);// 构造实体实例
foreach (var prop in props)
{
if (prop.Name == "id")
{

// PropertyInfo.SetValue 设置指定对象的属性值。
// t 将设置其属性值的对象。
// item.Key 新的属性值。
prop.SetValue(t, item.Key, null);
}
else
{
if (item.Value.ContainsKey(prop.Name))
{
var value = Utils.GetValue(item.Value[prop.Name], prop.PropertyType);
prop.SetValue(t, value, null);
}
}
}
// Dictionary<int, T>
// MethodBase.Invoke 调用由此MethodInfo实例反射的方法或构造函数。
// 使用指定参数调用由当前实例表示的方法或构造函数。
// result 在其上调用方法或构造函数的对象。
// new object[] { item.Key, t } 调用方法或构造函数的参数列表。

// 即调用Add方法,存入数据
dicType.GetMethod("Add").Invoke(result, new object[] { item.Key, t });
}
}
}
catch (Exception ex)
{
LoggerHelper.Error("FormatData Error: " + fileName + " " + ex.Message);
}

return result;
}

#endregion
}

public static void Init(Action<int, int> progress = null, Action finished = null)
{

}

/// <summary>
/// 加载数据逻辑
/// </summary>
/// <param name="gameDataType">加载数据列表</param>
/// <param name="formatData">处理数据方法</param>
/// <param name="progress">数据处理进度</param>
private void LoadData(List<Type> gameDataType, Func<string, Type, Type, object> formatData, Action<int, int> progress)
{
var count = gameDataType.Count;
var i = 1;

foreach (var item in gameDataType)
{
// BindingFlags.DeclaredOnly,仅搜索Type上声明的成员,而不搜索被简单继承的成员。
var p = item.GetProperty("dataMap", ~BindingFlags.DeclaredOnly);

var fileNameField = item.GetField("fileName");

if (p != null && fileNameField != null)
{
var fileName = fileNameField.GetValue(null) as String;

// public abstract Type PropertyType { get; }
var result = formatData(String.Concat(m_resourcePath, fileName, m_fileExtention), p.PropertyType, item);
// GetSetMethod()返回此属性的公共set访问器。
// public abstract System.Reflection.MethodInfo GetSetMethod (bool nonPublic);

// MethodInfo.Invoke(Object, Object[])
// public object Invoke (object obj, object[] parameters);
p.GetSetMethod().Invoke(null, new object[]{result});
if (progress != null)
progress(i, count);
i++;
}
}
}
}

public abstract class DataLoader
{
protected static readonly bool m_isPreloadData = true;
protected readonly String m_resourcePath; // 资源路径
protected readonly String m_fileExtention; // 文件扩展名
protected readonly bool m_isUseOutterConfig;
protected Action<int, int> m_progress; // 加载进度
protected Action m_finished; // 完成后的回调

protected DataLoader()
{
m_isUseOutterConfig = SystemConfig.IsUseOutterConfig;
if (m_isUseOutterConfig)
{
m_resourcePath = String.Concat(SystemConfig.OutterPath, SystemConfig.CONFIG_SUB_FOLDER);
m_fileExtention = SystemConfig.XML;
}
else
{
m_resourcePath = SystemConfig.CONFIG_SUB_FOLDER;//兼容文件模块
m_fileExtention = SystemConfig.CONFIG_FILE_EXTENSION;
}
}
}

暗黑战神\client\MogoSolution\GameData\FXData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:FXData
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:特效配置实体。
//----------------------------------------------------------------*/
#endregion
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Mogo.GameData
{
public class FXData : GameData<FXData>
{
public string player { get; set; }
/// <summary>
/// 是否一直保留。0不保留,1保留
/// </summary>
public FXStatic isStatic { get; set; }
/// <summary>
/// 是否与其他特效冲突。0不冲突,1冲突
/// </summary>
public FXConflict isConflict { get; set; }
/// <summary>
/// 0为普通,1为飞行物
/// </summary>
public EffectType effectType { get; set; }
public FXLocationType locationType { get; set; }
public int level { get; set; }
public int group { get; set; }
public Vector3 location { get; set; }
public String slot { get; set; }
public String resourcePath { get; set; }
public float duration { get; set; }
public String weaponTailSlot { get; set; }
public String weaponTailMaterial { get; set; }
public int weaponTailEmitTime { get; set; }
public float weaponTailLeftTime { get; set; }
public int weaponTailDurationTime { get; set; }
public int weaponTailSubdivisions { get; set; }
public String anim { get; set; }
public int soundDelay { get; set; }
public int fadeDelay { get; set; }
public int fadeDulation { get; set; }
public float fadeStart { get; set; }
public float fadeEnd { get; set; }
public String shader { get; set; }
public List<float> rimWidth { get; set; }
public List<float> rimPower { get; set; }
public List<float> finalPower { get; set; }
public List<float> r { get; set; }
public List<float> g { get; set; }
public List<float> b { get; set; }
public List<float> a { get; set; }
public List<int> shaderDuration { get; set; }

public String dissonShader { get; set; }
public String nosieTexture { get; set; }
public float nosieOffetFrom { get; set; }
public float nosieOffetTo { get; set; }
public Color dissonColor { get; set; }
public int dissonDuration { get; set; }
public int dissonDelay { get; set; }

// xml文件路径,这个字段会在加载xml使用
public static readonly string fileName = "xml/FXData";
}

public enum EffectType : byte
{
Normal = 0,
Flying = 1,
}

public enum FXConflict : byte
{
NotConflict = 0,
Conflict = 1,
}

public enum FXStatic : byte
{
NotStatic = 0,
Static = 1
}

public enum FXLocationType : byte
{
World = 0,
SelfLocal = 1,
SelfSlot = 2,
SelfWorld = 3,
SlotWorld = 4
}
}

使用FXData

1、FXData.dataMap.ContainsKey(hitFx)
2、Linq

1
FXData.dataMap.Values.Where(t => t.player.Contains(playerName) && !String.IsNullOrEmpty(t.resourcePath) && !t.resourcePath.Contains(weaponType)).Select(t => t.resourcePath)

1
var fx = FXData.dataMap.Get(id);

MogoSolution

\client\MogoSolution\LoaderLib\Utils\SystemConfig.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:SystemConfig
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:系统参数配置。
//----------------------------------------------------------------*/
#endregion

using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Linq;
using UnityEngine;

namespace Mogo.Util
{
/// <summary>
/// 系统参数配置。
/// </summary>
// C#可以将类、结构或接口的定义拆分到两个或多个源文件中,在类声明前添加partial关键字即可。
// (一)什么情况下使用分部类?
// 处理大型项目时,使一个类分布于多个独立文件中可以让多位程序员同时对该类进行处理(相当于支持并行处理,很实用);
public partial class SystemConfig
{
#region 常量
public const string XML = ".xml";
public const string CONFIG_SUB_FOLDER = "data/";
#endregion

public Dictionary<ulong, String> GuideTimes{ get;set; }

public static bool IsUseOutterConfig
{
get
{
LoggerHelper.Debug("Application.platform: " + Application.platform);
if (Application.platform == RuntimePlatform.Android)
{
if (Directory.Exists(String.Concat(AndroidPath, CONFIG_SUB_FOLDER)))
return true;
}
else if (Application.platform == RuntimePlatform.IPhonePlayer)
{
if (Directory.Exists(String.Concat(IOSPath, CONFIG_SUB_FOLDER)))
return true;
}
else if (Application.platform == RuntimePlatform.WindowsPlayer)
{
if (Directory.Exists(String.Concat(PCPath, CONFIG_SUB_FOLDER)))
return true;
}
return false;
}
}

/// <summary>
/// 外部资源目录。
/// </summary>
public static String OutterPath
{
get
{
LoggerHelper.Debug("Application.platform: " + Application.platform);
if (Application.platform == RuntimePlatform.Android)
return AndroidPath;
else if (Application.platform == RuntimePlatform.IPhonePlayer)
return IOSPath;
else if (Application.platform == RuntimePlatform.WindowsPlayer)
return PCPath;
else if (Application.platform == RuntimePlatform.WindowsEditor)
return PCPath;
else if (Application.platform == RuntimePlatform.OSXEditor)
return IOSPath;
else
return "";
}
}
}
}

MogoSolution\Common\Utils\MecanimEvent.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:MecanimEvent
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:动作事件触发器。
//----------------------------------------------------------------*/
#endregion

using System;
using System.Collections;
using UnityEngine;

public class MecanimEvent
{
private Animator m_animator;
private AnimationClip m_currentMark;
private bool m_isNotFadeing = true;

private float passTime = 0; // 当前帧累积时间

/// <summary>
/// 构造函数。
/// </summary>
/// <param name="animator">Mecanim动作系统对象。</param>
public MecanimEvent(Animator animator)
{
m_animator = animator;
}

/// <summary>
/// 监测动作状态变迁。
/// </summary>
/// <param name="StateChanged">动画状态变化事件。
/// String: 动画名称。
/// Boolean: 动画变化状态。true: 动画开始;false: 动画结束。
/// </param>
/// <returns>迭代器。</returns>
public IEnumerator CheckAnimationChange(Action<string, Boolean> StateChanged)
{
while(true)
{
var state = m_animator.GetCurrentAnimationClipState(0); // 获取当前播放动作状态
if(state.Length != 0)
{
if (state[0].weight != 1) // 当前动作正在融合
{
// 判断已标记为融合,如果不是融合,则表示为刚开始融合,此刻为下一个动作的开始点
if (m_isNotFadeing)
{
var nextState = m_animator.GetNextAnimationClipState(0);
if (StateChanged != null && nextState.Length != 0)
{
StateChanged(nextState[0].clip.name, true);
}
m_currentMark = state[0].clip; // 更新当前动作
m_isNotFadeing = false; // 标记为融合状态
// 等待直到下一个固定帧速率更新函数。
yield return new WaitForFixedUpdate();
}
}
else
{
// 在动作变迁时,state会瞬间变为新动作,weight变为1,所以需判断state是否已改变,此刻为旧动作结束点
if (m_currentMark != state[0].clip)
{
if (m_currentMark != null && StateChanged != null)
{
StateChanged(m_currentMark.name, false);
}
m_currentMark = state[0].clip; ;// 更新当前动作
m_isNotFadeing = true; // 标记为非融合状态

yield return new WaitForFixedUpdate();
}
}

}
yield return new WaitForFixedUpdate();
}
}

/// <summary>
/// 监测动作状态变迁。
/// </summary>
/// <param name="StateChanged">动画状态变化事件。
/// String: 动画名称。
/// Boolean: 动画变化状态。true: 动画开始;false: 动画结束。
/// </param>
/// <returns>迭代器。</returns>
public IEnumerator CheckAnimationChange(Action<int, bool> StateChanged)
{
while (true)
{
var state = m_animator.GetCurrentAnimatorStateInfo(0);//获取当前播放动作状态
passTime = passTime + Time.deltaTime;
if (passTime >= state.length)
{
StateChanged(state.nameHash, state.loop);
passTime = 0;
yield return new WaitForFixedUpdate();
}
yield return new WaitForFixedUpdate();
}
}
}

\MogoSolution\LoaderLib\Utils\Timer\TimerData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System;

namespace Mogo.Util
{
/// <summary>
/// 定时器抽象实体
/// </summary>
// 程序集就是一个项目,多个项目构成一个解决方案。internal的权限小于public
// 访问级别: private(私人,类内) < protected(家内,类和子类内) < internal (族内,项目内)< public(公家,无限制)
internal abstract class AbsTimerData
{
private uint m_nTimerId; // 任务id

public uint NTimerId
{
get { return m_nTimerId; }
set { m_nTimerId = value; }
}

private int m_nInterval; // 间隔多少秒,重复这个任务

public int NInterval
{
get { return m_nInterval; }
set { m_nInterval = value; }
}

private ulong m_unNextTick; // 下一次触发的时间点

public ulong UnNextTick
{
get { return m_unNextTick; }
set { m_unNextTick = value; }
}

// Delegate是个类,基类,抽象类。delegate是一个关键字
// 此处的Action是个属性名,set的时候返回委托
public abstract Delegate Action
{
get;
set;
}

public abstract void DoAction();
}

/// <summary>
/// 无参数定时器实体
/// </summary>
internal class TimerData : AbsTimerData
{
private Action m_action;

// 重写委托
public override Delegate Action
{
get { return m_action; }
set { m_action = value as Action; }
}

public override void DoAction()
{
// 执行委托
m_action();
}
}

/// <summary>
/// 1个参数定时器实体
/// </summary>
/// <typeparam name="T">参数1</typeparam>
internal class TimerData<T> : AbsTimerData
{
private Action<T> m_action;

public override Delegate Action
{
get { return m_action; }
set { m_action = value as Action; }
}

private T m_arg1;

public T Arg1
{
get { return m_arg1; }
set { m_arg1 = value; }
}

public override void DoAction()
{
m_action(m_arg1);
}
}

internal class TimerData<T, U> : AbsTimerData
{
private Action<T, U> m_action;

public override Delegate Action
{
get { return m_action; }
set { m_action = value as Action<T, U>; }
}

private T m_arg1;

public T Arg1
{
get { return m_arg1; }
set { m_arg1 = value;}
}

private U m_arg2;

public U Arg2
{
get { return m_arg2; }
set { m_arg2 = value; }
}

public override void DoAction()
{
m_action(m_arg1, m_arg2);
}
}
}

TimerHeap.cs heap堆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:TimerHeap
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:定时触发器。
//----------------------------------------------------------------*/
#endregion

using MS;
using System;
using System.Diagnostics;

namespace Mogo.Util
{
/// <summary>
/// 定时触发器
/// </summary>
public class TimerHeap
{
private static uint m_nNextTimerId;
private static uint m_unTick;
// 带键值的优先队列
private static KeyedPriorityQueue<uint, AbsTimerData, ulong> m_queue;
private static Stopwatch m_stopWatch;
private static readonly object m_queueLock = new object();

/// <summary>
/// 私有构造函数,封闭实例化。
/// </summary>
private TimerHeap() { }

/// <summary>
/// 默认构造函数
/// </summary>
static TimerHeap()
{
m_queue = new KeyedPriorityQueue<uint, AbsTimerData, ulong>();
m_stopWatch = new Stopwatch();
}

/// <summary>
/// 添加定时对象
/// </summary>
/// <param name="start">延迟启动时间。(毫秒)</param>
/// <param name="interval">重复间隔,为零不重复。(毫秒)</param>
/// <param name="handler">定时处理方法</param>
/// <returns>定时对象Id</returns>
public static uint AddTimer(uint start, int interval, Action handler)
{
//起始时间会有一个tick的误差,tick精度越高,误差越低
var p = GetTimerData(new TimerData(), start, interval);
p.Action = handler;
return AddTimer(p);
}

/// <summary>
/// 添加定时对象
/// </summary>
/// <typeparam name="T">参数类型1</typeparam>
/// <param name="start">延迟启动时间。(毫秒)</param>
/// <param name="interval">重复间隔,为零不重复。(毫秒)</param>
/// <param name="handler">定时处理方法</param>
/// <param name="arg1">参数1</param>
/// <returns>定时对象Id</returns>
public static uint AddTimer<T>(uint start, int interval, Action<T> handler, T arg1)
{
var p = GetTimerData(new TimerData<T>(), start, interval);
p.Action = handler;
p.Arg1 = arg1;
return AddTimer(p);
}

/// <summary>
/// 添加定时对象
/// </summary>
/// <typeparam name="T">参数类型1</typeparam>
/// <typeparam name="U">参数类型2</typeparam>
/// <param name="start">延迟启动时间。(毫秒)</param>
/// <param name="interval">重复间隔,为零不重复。(毫秒)</param>
/// <param name="handler">定时处理方法</param>
/// <param name="arg1">参数1</param>
/// <param name="arg2">参数2</param>
/// <returns>定时对象Id</returns>
public static uint AddTimer<T, U>(uint start, int interval, Action<T, U> handler, T arg1, U arg2)
{
var p = GetTimerData(new TimerData<T, U>(), start, interval);
p.Action = handler;
p.Arg1 = arg1;
p.Arg2 = arg2;
return AddTimer(p);
}

/// <summary>
/// 添加定时对象
/// </summary>
/// <typeparam name="T">参数类型1</typeparam>
/// <typeparam name="U">参数类型2</typeparam>
/// <typeparam name="V">参数类型3</typeparam>
/// <param name="start">延迟启动时间。(毫秒)</param>
/// <param name="interval">重复间隔,为零不重复。(毫秒)</param>
/// <param name="handler">定时处理方法</param>
/// <param name="arg1">参数1</param>
/// <param name="arg2">参数2</param>
/// <param name="arg3">参数3</param>
/// <returns>定时对象Id</returns>
public static uint AddTimer<T, U, V>(uint start, int interval, Action<T, U, V> handler, T arg1, U arg2, V arg3)
{
var p = GetTimerData(new TimerData<T, U, V>(), start, interval);
p.Action = handler;
p.Arg1 = arg1;
p.Arg2 = arg2;
p.Arg3 = arg3;
return AddTimer(p);
}

/// <summary>
/// 删除定时对象
/// </summary>
/// <param name="timerId">定时对象Id</param>
public static void DelTimer(uint timerId)
{
lock (m_queueLock)
m_queue.Remove(timerId);
}

/// <summary>
/// 周期调用触发任务
/// </summary>
public static void Tick()
{
m_unTick += (uint)m_stopWatch.ElapsedMilliseconds;
m_stopWatch.Reset();
m_stopWatch.Start();

while (m_queue.Count != 0)
{
AbsTimerData p;
lock (m_queueLock)
p = m_queue.Peek();
if (m_unTick < p.UnNextTick)
{
break;
}
lock (m_queueLock)
m_queue.Dequeue();
if (p.NInterval > 0)
{
p.UnNextTick += (ulong)p.NInterval;
lock (m_queueLock)
m_queue.Enqueue(p.NTimerId, p, p.UnNextTick);
p.DoAction();
}
else
{
p.DoAction();
}
}
}

/// <summary>
/// 重置定时触发器
/// </summary>
public static void Reset()
{
m_unTick = 0;
m_nNextTimerId = 0;
lock (m_queueLock)
while (m_queue.Count != 0)
m_queue.Dequeue();
}

private static uint AddTimer(AbsTimerData p)
{
lock (m_queueLock)
m_queue.Enqueue(p.NTimerId, p, p.UnNextTick);
return p.NTimerId;
}

private static T GetTimerData<T>(T p, uint start, int interval) where T : AbsTimerData
{
p.NInterval = interval;
p.NTimerId = ++m_nNextTimerId;
p.UnNextTick = m_unTick + 1 + start;
return p;
}
}
}

Actors

Actor开头的脚本都是挂载到游戏对象上的。

\Actors\ActorParent.cs 需要继承MonoBehavior

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// Actor对象的基类
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

using Mogo.Game;
using Mogo.GameData;
using Mogo.Util;
using Object = UnityEngine.Object;

public class ActorParent<T> : ActorParent where T : EntityParent
{
private T m_theEntity;
private T theEntity
{
get { return m_theEntity; }
set { m_theEntity = value; }
}

public override EntityParent GetEntity()
{
return m_theEntity;
}
}

public class ActorParent : MonoBehaviour
{
public const string ON_EQUIP_DONE = "ActorParent.ON_EQUIP_DONE";

private MecanimEvent m_mecanimEvent;
protected Animator m_animator;
protected string preActionName = "";
public Action<string, string> ActChangeHandle;

private Action<String, Boolean> m_animatorStateChanged;
public Action<String, Boolean> AnimatorStateChanged
{
get
{
return m_animatorStateChanged;
}
set
{
m_animatorStateChanged = value;
}
}

private Action<String, Boolean> m_hitStateChanged;
public Action<String, Boolean> HitStateChanged
{
get
{
return m_hitStateChanged;
}
set
{
m_hitStateChanged = value;
}
}

public bool isNeedInitEquip = true;
private uint m_checkAnimationChangeTimeHeapId = uint.MaxValue;

virtual public EntityParent GetEntity()
{
return null;
}

// 初始化
virtual protected void Awake()
{
m_animator = GetComponent<Animator>();
if (m_animator)
{
m_mecanimEvent = new MecanimEvent(m_animator);
m_checkAnimationChangeTimeHeapId = TimerHeap.AddTimer(500, 0, StartCheckAnimationChange);
}

EventDispatcher.AddEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, InstanceLoaded);

//Debug.LogError("ActorParent Awake" + tag);
}

void OnDestroy()
{
RemoveOld();
EventDispatcher.RemoveEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, InstanceLoaded);
TimerHeap.DelTimer(m_checkAnimationChangeTimeHeapId);
StopCoroutine("CheckAnimationChange");
}

private int idleCnt = 0;

// 行为改变,子类调用或重写
// 子类的Update里每帧调用
virtual public void ActChange()
{
if (m_animator == null)
{
return;
}

if (m_animator.IsInTransition(0)) // 融合期间
{
return;
}

var state = m_animator.GetCurrentAnimationClipState(0);

if (state.Length == 0)
{
return;
}

string currName = state[0].clip.name;

if (currName != preActName) // 动作变换
{
if(ActChangeHandle != null)
{
ActChangeHandle(preActName, currName);
}

if (!currName.EndsWith("ready") && !currName.EndsWith("run") && !currName.EndsWith("idle") && !currName.EndsWith("powercharge") &&
!currName.EndsWith("powercharge_loop") && !currName.EndsWith("powercharge_left") &&
!currName.EndsWith("roll"))
{
SkillAction a = null;
if (GetEntity().currSpellID != -1 &&
SkillAction.dataMap.TryGetValue(SkillData.dataMap[GetEntity().currSpellID].skillAction[0], out a))
{// 只为当前版本所用,新版本中动作不一样了要去掉
if (a.duration <= 0)
{
m_animator.SetInteger("Action", 0);
}
else
{// 旋风斩之类技能使用
m_animator.SetInteger("Action", -3);
}
}
else
{
m_animator.SetInteger("Action", 0);
}
}
preActName = currName;
}
if ((currName.EndsWith("hit") && preActName.EndsWith("hit")) ||
(currName.EndsWith("push") && preActName.EndsWith("push")) ||
(currName.EndsWith("hitair") && preActName.EndsWith("hitair")) ||
(currName.EndsWith("knockdown") && preActName.EndsWith("knockdown")))
{
int act = m_animator.GetInteger("Action");
if (act != 0 && act != -1)
{
m_animator.SetInteger("Action", 0);
}
}
if (GetEntity() != null &&
currName != null &&
GetEntity().stiff &&
(currName.EndsWith("ready") ||
currName.EndsWith("run") ||
currName.EndsWith("run_left") ||
currName.EndsWith("run_right") ||
currName.EndsWith("run_back")))
{
idleCnt++;
if (idleCnt > 5)
{
GetEntity().ClearHitAct();
idleCnt = 0;
}
}
else
{
idleCnt = 0;
}
}

virtual public void Release()
{
RemoveAll();
EventDispatcher.RemoveEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, InstanceLoaded);
TimerHeap.DelTimer(m_checkAnimationChangeTimeHeapId);
StopCoroutine("CheckAnimationChange");
}

virtual public void Attack(int _SpellID)
{
}

virtual public void Idle()
{
}

virtual public void OnHit(int _SpellID)
{
}

virtual public void Walk()
{
}

// 释放AnimatorController
public void ReleaseController()
{
if(m_animator != null)
{
if (m_animator.runtimeAnimatorController != null)
AssetCacheMgr.ReleaseResource(m_animator.runtimeAnimatorController);
m_animator = null;
}
}

public void RemoveOld()
{
for (int i = 0; i < m_equipList.Count; i++)
{
AssetCacheMgr.ReleaseInstance(m_equipList[i], false);
m_equipList[i] = null;
}

for (int i = 0; i < m_equipMeshOrMaterialList.Count; i++)
{
AssetCacheMgr.ReleaseResource(m_equipMeshOrMaterialList[i], false);
m_equipMeshOrMaterialList[i] = null;
}

for (int i = 0; i < m_smrList.Count; i++)
{
// SkinnedMeshRenderer.sharedMaterial
// The shared material of this object.
m_smrList[i].sharedMaterial = null;
// SkinnedMeshRenderer.sharedMesh
// 用于蒙皮的网格。
m_smrList[i].sharedMesh = null;
m_smrList[i] = null;
}

m_smrList.Clear();
m_equipList.Clear();
m_equipMeshOrMaterialList.Clear();
}

private void StartCheckAnimationChange()
{
if (this && this.gameObject.activeSelf)
StartCoroutine("CheckAnimationChange");
}

private IEnumerator CheckAnimationChange()
{
return m_mecanimEvent.CheckAnimationChange(OnStateChanged);
}

private void OnStateChanged(string name, bool isStart)
{
if (AnimatorStateChanged != null)
{
AnimatorStateChanged(name, isStart);
}
if (HitStateChanged != null)
{
HitStateChanged(name, isStart);
}
}

private void ClearOriginalModel()
{
if (m_smrAllList.Count <= 0)
{
foreach(Transform t in transform)
{
SkinnedMeshRenderer smr = t.GetComponent<SkinnedMeshRenderer>();
if (smr == null) continue;
smr.gameObject.SetActive(true);
smr.sharedMesh = null;
m_smrAllList.Add(smr);
}
}
}

private void GetEquipObjectList(List<EquipData> equipDataList, Action<Dictionary<int, EquipObjectData>, Dictionary<int, int>> onLoad)
{
HashSet<int> equipIdSet = new HashSet<int>();


}
}

\client\Assets\Scripts\Actors\ActorPlayer.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
using UnityEngine;
using System.Collections;

using Mogo.Util;
using Mogo.Game;
using Mogo.GameData;

public class ActorPlayer : ActorPlayer<EntityPlayer> { }

public class ActorPlayer<T> : ActorParent<T> where T : EntityPlayer
{
protected Transform m_billboardTrans;
protected Transform m_wingBone;

GameObject m_goWing;

void Start()
{
if (isNeedInitEquip) InitEquipment();

m_billboardTrans = transform.FindChild("slot_billboard");
}

void Update()
{
ActChange();
if (m_billboardTrans != null && theEntity != null)
{
BillboardViewManager.Instance.UpdateBillboardPos(m_billboardTrans.position, theEntity.ID);
}
}

private string currWing;

public void AddWing(string wingName, System.Action callBack)
{
if (m_wingBone == null)
{
m_wingBone = transform.FindChild("Bip_master/Bip001 Pelvis/Bip001 Spine/bip_wing");
}

if (currWing == wingName) return;

currWing = wingName;

AssetCacheMgr.GetInstance(
wingName,
(name, id, obj) =>
{
m_goWing = (GameObject)obj;
m_goWing.transform.parent = m_wingBone;
m_goWing.transform.localPosition = Vector3.zero;
m_goWing.transform.localEulerAngles = new Vector3(0, 90, 90);

switch(theEntity.vocation)
{
case Vocation.Warrior:
m_goWing.transform.localScale = new Vector3(1.7f, 1.7f, 1.7f);
break;

case Vocation.Mage:
m_goWing.transform.localScale = new Vector3(1.3f, 1.3f, 1.3f);
break;

case Vocation.Assassin:
m_goWing.transform.localScale = new Vector3(1.15f, 1.15f, 1.15f);
break;
case Vocation.Archer:
m_goWing.transform.localScale = new Vector3(1.3f, 1.3f, 1.3f);
break;
}

if (callBack != null)
callBack();
}
);
}

public void RemoveWing()
{
if(m_goWing)
{
currWing = "";
AssetCacheMgr.ReleaseInstance(m_goWing);
m_goWing = null;
}
}

public void SetLayer(int layer)
{
SetObjectLayer(layer, m_goWing);
}

public void SetObjectLayer(int layer, GameObject obj)
{
if (!obj)
return;

obj.layer = layer;

foreach (Transform item in obj.transform)
{
SetObjectLayer(layer, item.gameObject);
}
}
}

\client\Assets\Scripts\Actors\ActorDummy.cs 纯客户端的怪物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using UnityEngine;
using System.Collections;

using Mogo.Game;
using Mogo.Util;
using Mogo.FSM;

public class ActorDummy : ActorParent<EntityDummy>
{
protected Transform m_billboardTrans;

protected override void Awake()
{
m_billboardTrans = transform.FindChild("slot_billboard");
base.Awake();
}

void Update()
{
ActChange();

if (m_billboardTrans != null)
{
BillboardViewManager.Instance.UpdateBillboardPos(m_billboardTrans.position, theEntity.ID);
}
}
}

\Actors\ActorBullet.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 飞行物控制器(用于火球,弓箭等物体的特效播放,追踪目标的显示逻辑
using UnityEngine;
using System.Collections;
using Mogo.GameData;
using Mogo.Util;

// 动态绑定到游戏对象上
public class ActorBullet : MonoBehaviour
{
public float speed = 10; // 移动速度
public Transform target; // 目标
public Vector3 targetPosition; // 目标位置
public bool isSetup = false; // 是否设置了目标

// 销毁回调,外界可定制
public System.Action OnDestroy = null;

/// <summary>
/// 是否设置了目标
/// </summary>
/// <param name="target">目标</param>
/// <param name="speed">子弹移动速度</param>
/// <param name="targetPosition">目标位置</param>
public void Setup(Transform target, float speed, Vector3 targetPosition)
{
this.target = target;

this.targetPosition = targetPosition;

if (target != null)
{
targetPosition = target.position;
}
this.speed = speed;

isSetup = true;
}

void Update()
{
if (!isSetup) return;

// 射向目标
if (target != null)
{
targetPosition = target.position;
}
transform.LookAt(targetPosition);

float step = speed * Time.deltaTime; // 速度 * 时间 = 距离
float distance = Vector3.Distance(targetPosition, transform.position);

if (distance < step) // 到达目标
{
transform.position = targetPosition;
// 销毁
if (OnDestroy != null)
OnDestroy();
}
else
{
// 距离 * 方向
transform.TransLate(step * transform.forward, Space.World);
}
}
}

GUI

\client\Assets\Scripts\GUI\MogoFx\MogoCameraCullByLayer.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MogoCameraCullByLayer : MonoBehaviour
{
public List<int> LayerList;
public List<float> DistanceList;

void Start()
{
// 定义大小为32的一维数组,用来存储所有层的剔除信息
float[] distance = new float[32];

for (int i = 0; i < LayerList.Count; ++i)
{
// 设置每层的剔除距离
distance[LayerList[i]] = DistanceList[i];
}
/*
Camera.layerCullDistances属性用来设置摄像机基于层的消隐距离。摄像机可以通过基于层(GameObject.layer)的方式来设置不同层物体的消隐距离,但这个距离必须小于或等于摄像机的farClipPlane才有效。

相机跟每一层的剔除距离。比如,在视野中有很多npc,可以把npc设置到npc层,并在代码中为npc层设置较小的layerCullDistances剔除距离,这样就可以只渲染npc层剔除距离内的npc,减少性能开销。
*/

// 将数组赋给摄像机的layerCullDistances
Camera.main.layerCullDistances = distance;
// 在相机转动时,不会影响哪些对象可见或不可见,也就是不会转动中,有些原来显示的对象被隐藏
Camera.main.layerCullSpherical = true;
}
}

GUI/Mogo/UILogicManager.cs UI逻辑绑定管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
using Mogo.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/// <summary>
/// UI逻辑绑定管理。
/// </summary>
public abstract class UILogicManager
{
private HashSet<INotifyPropChanged> m_itemSources = new HashSet<INotifyPropChanged>();
private EventController m_eventController;

/// <summary>
/// 绑定数据源
/// </summary>
public INotifyPropChanged ItemSource
{
set
{
if (value != null && !m_itemSources.Contains(value))
{
m_itemSources.Add(value);
value.SetEventController(m_eventController);
value.AddUnloadCallback(() =>
{
if (m_itemSources != null && m_itemSources.Contains(value))
{
m_itemSources.Remove(value);
}
});
}
}
}

/// <summary>
/// 默认构造函数。
/// </summary>
public UILogicManager()
{
m_eventController = new EventController();
}

/// <summary>
/// 设置绑定。
/// </summary>
/// <typeparam name="T">绑定参数类型</typeparam>
/// <param name="key">绑定关键字</param>
/// <param name="action">绑定调用方法</param>
public void SetBinding<T>(String key, Action<T> action)
{
if (m_eventController.ContainsEvent(key))
return;
m_eventController.AddEventListener(key, action);
}

/// <summary>
/// 根据数据源更新UI。(效率不高,不要频繁调用)
/// </summary>
public void UpdateUI()
{
foreach (var itemSource in m_itemSources)
{
if (itemSource != null)
{
var type = itemSource.GetType();
//获取带一个参数回调方法的TriggerEvent。
var mTriggerEvent = m_eventController.GetType().GetMethods().FirstOrDefault(t => t.Name == "TriggerEvent" && t.IsGenericMethod && t.GetGenericArguments().Length == 1);
foreach (var item in m_eventController.TheRouter)
{
var prop = type.GetProperty(item.Key);
if (prop == null)
continue;
var method = mTriggerEvent.MakeGenericMethod(prop.PropertyType);//对泛型进行类型限定
var value = prop.GetGetMethod().Invoke(itemSource, null);//获取参数值
method.Invoke(m_eventController, new object[] { item.Key, value });//调用对应参数类型的触发方法
}
}
}
}

/// <summary>
///
/// </summary>
public virtual void Release()
{
// 不注释:清空属性监听, 需要在UI重新Load的时候重新设置属性监听ItemSource
// 注释:保留属性监听,不需要重新设置ItemSource
foreach (var item in m_itemSources)
{
if (item != null)
item.RemoveEventController(m_eventController);
}
m_itemSources.Clear(); // 如果RemoveEventController,需要把m_itemSources列表同时清空,否则无法重新设置EventController

// 需要清空,在UI重新Load的时候重新添加监听
m_eventController.Cleanup();
}
}


public class MogoUIBehaviour : MFUIUnit
{

}

/// <summary>
/// 向客户端发出某一属性值已更改的通知。
/// </summary>

public interface INotifyPropChanged
{
/// <summary>
/// 设置事件控制器。
/// </summary>
/// <param name="controller"></param>
void SetEventController(EventController controller);

/// <summary>
/// 移除事件控制器。
/// </summary>
/// <param name="controller"></param>
void RemoveEventController(EventController controller);

/// <summary>
/// 在更改属性值时发生。
/// </summary>
/// <typeparam name="T">属性类型。</typeparam>
/// <param name="propertyName">属性名称。</param>
/// <param name="value">属性值。</param>
void OnPropertyChanged<T>(string propertyName, T value);

/// <summary>
/// 监听实体资源释放回调。
/// </summary>
/// <param name="onUnload">回调事件处理</param>
void AddUnloadCallback(Action onUnload);
}

public abstract class NotifyPropChanged
{
private HashSet<EventController> m_uiBindingSet = new HashSet<EventController>();
private Action m_onUnload;

/// <summary>
/// 添加UI事件绑定。
/// </summary>
/// <param name="controller"></param>
public void SetEventController(EventController controller)
{
m_uiBindingSet.Add(controller);
}

/// <summary>
/// 移除UI事件绑定。
/// </summary>
/// <param name="controller"></param>
public void RemoveEventController(EventController controller)
{
m_uiBindingSet.Remove(controller);
}

/// <summary>
/// 属性值变化处理。
/// </summary>
/// <typeparam name="T">属性值类型。</typeparam>
/// <param name="propertyName">属性名称。</param>
/// <param name="value">属性值。</param>
public void OnPropertyChanged<T>(string propertyName, T value)
{
foreach (var item in m_uiBindingSet)
{
if (item != null)
item.TriggerEvent(propertyName, value);
}
}

/// <summary>
/// 监听实体资源释放回调。
/// </summary>
/// <param name="onUnload">回调事件处理</param>
public void AddUnloadCallback(Action onUnload)
{
m_onUnload = onUnload;
}

/// <summary>
/// 清理绑定数据。
/// </summary>
protected void ClearBinding()
{
if (m_onUnload != null)
m_onUnload();
m_uiBindingSet.Clear();
}
}

GameLogic

\client\Assets\Scripts\GameLogic\IEventManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:IEventManager
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:子系统事件接口
//----------------------------------------------------------------*/
#endregion

namespace Mogo.Game
{
public interface IEventManager
{
/// <summary>
/// 子系统事件接口
/// </summary>
void AddEventListener();
/// <summary>
/// 移除事件订阅。
/// </summary>
void RemoveListeners();
}
}

EntityParent.cs 客户端Entity基类,这是个partial类,方法在这里,属性在ParentProperty.cs里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
using UnityEngine;
using System;
using System.Collections.Generic;

using Mogo.FSM;
using Mogo.GameData;
using Mogo.Util;
using Mogo.RPC;
using Mogo.Task;

namespace Mogo.Game
{
/// <summary>
/// 逻辑控制类,这里处理Entity数据变化,状态变化等逻辑
/// </summary>
public partial class EntityParent : NotifyPropChanged, INotifyPropChanged
{
#region 虚方法

virtual public void MainCameraCompleted()
{

}

virtual public void CreateModel()
{
if (GameObject)
{
MogoWorld.GameObjects.Add(GameObject.GetInstanceID(), this);
}
}

virtual public void CreateActualModel()
{

}

virtual public void CreateDefaultModel()
{

}

virtual public void ApplyRootMotion(bool b)
{
if (animator == null)
{
return;
}
animator.applyRootMotion = b;
}

virtual public void SetAction(int act)
{
if (animator == null)
{
return;
}

animator.SetInteger("Action", act);

if (weaponAnimator)
{
weaponAnimator.SetInteger("Action", act);
}

if (act == ActionConstants.HIT_AIR)
{
stiff = true; // 僵硬的
hitAir = true;
}
else if (act == ActionConstants.KNOCK_DOWN)
{
stiff = true;
knockDown = true;
}
else if (act == ActionConstants.HIT)
{
stiff = true;
}
else if (act == ActionConstants.PUSH)
{
stiff = true;
}
else if (act == ActionConstants.HIT_GROUND)
{
stiff = true;
hitGround = true;
}
}

private void HitStateChange(string name, bool start)
{
if ((name.EndsWith("ready") || name.EndsWith("run")) && start)
{
Actor.HitStateChanged = null;
ClearHitAct();
}
}

public void ClearHitAct()
{
if (this is EntityMyself)
{
currSpellID = -1; // 用于攻击中受击打断后的再次容错
}
ChangeMotionState(MotionState.IDLE);
hitAir = false;
knockDown = false;
hitGround = false;
stiff = false;
EventDispatcher.TriggerEvent(Events.AIEvent.DummyStiffEnd, Transform.gameObject);
}

private void DelayCheck()
{
if (animator == null)
{
return;
}
if (CurrentMotionState == MotionState.HIT && animator.GetInteger("Action") == 0)
{
ClearHitAct();
}
if (stiff && animator.GetInteger("Action") == 0)
{
ClearHitAct();
}
}

virtual public void SetSpeed(float speed)
{
if (animator == null)
{
return;
}
animator.SetFloat("Speed", speed);
}

virtual public bool IsInTransition()
{
return animator.IsInTransition(0);
}

virtual public void ChangeMotionState(string newState, params System.Object[] args)
{
fsmMotion.ChangeStatus(this, newState, args);
}

virtual public void ChangeMotionStateInFrames(string newState, params System.Object[] args)
{
fsmMotion.ChangeStatus(this, newState, args);
}

// 对象进入场景,在这里初始化各种数据, 资源, 模型等
// 传入数据。
virtual public void OnEnterWorld()
{
buffManager = new BuffManager(this);
EventDispatcher.AddEventListener<GameObject, Vector3>(MogoMotor.ON_MOVE_TO, OnMoveTo);
EventDispatcher.AddEventListener<GameObject, Vector3, float>(MogoMotor.ON_MOVE_TO_FALSE, OnMoveToFalse);
}

// 对象从场景中删除, 在这里释放资源
virtual public void OnLeaveWorld()
{
EventDispatcher.RemoveEventListener<GameObject, Vector3>(MogoMotor.ON_MOVE_TO, OnMoveTo);
EventDispatcher.RemoveEventListener<GameObject, Vector3, float>(MogoMotor.ON_MOVE_TO_FALSE, OnMoveToFalse);
if (buffManager != null)
{
buffManager.Clean();
}
RemoveListener();
ClearBinding();
if (GameObject)
{
MogoWorld.GameObjects.Remove(GameObject.GetInstanceID());
}

if (Actor)
Actor.ReleaseController();
GameObject.Destroy(GameObject);

GameObject = null;
Transform = null;
weaponAnimator = null;
animator = null;
motor = null;
sfxHandler = null;
audioSource = null;
}

virtual public void Idle()
{
if ((this is EntityMyself) && (this as EntityMyself).deathFlag == 1)
{
return;
}
if (battleManger == null)
{
ChangeMotionState(MotionState.IDLE);
}
else
{
this.battleManger.Idle();
}
}

virtual public void Roll()
{
this.battleManger.Roll();
}

#endregion

#region 公共方法

#region 技能相关
/// <summary>
/// 去除重力,会连动作的自带位移都去掉,慎用
/// (animator对带chactorController的物体自带一个重力,暂没找到更好的去除方法)
/// </summary>
public void RemoveGravity()
{
motor.gravity = 0;
}

public void SetGravity(float gravity = 20)
{
motor.gravity = gravity;
}

/// <summary>
/// 冻结
/// </summary>
public void SetFreeze()
{
if (this is EntityMyself)
{
motor.enableStick = false;
}
SetSpeedReduce();
}

/// <summary>
/// 解冻
/// </summary>
public void SetThaw()
{
SetSpeedRecover();
motor.enableStick = true;
}

// <summary>
/// 减速
/// </summary>
/// <param name="speedRate">减速率</param>
public void SetSpeedReduce(float speedRate = 0)
{
if (isSrcSpeed)
{
isSrcSpeed = false;
srcSpeed = animator.speed;
gearMoveSpeedRate = speedRate;
}
animator.speed = srcSpeed * speedRate;
}

/// <summary>
/// 恢复速度
/// </summary>
public void SetSpeedRecover()
{
if (!isSrcSpeed)
{
gearMoveSpeedRate = 1;
isSrcSpeed = true;
animator.speed = srcSpeed;
}
}


}
}

\client\Assets\Scripts\GameLogic\Entities\ParentPartial\ParentProperty.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using System;
using System.Collections.Generic;

using Mogo.FSM;
using Mogo.GameData;
using Mogo.Util;
using Mogo.RPC;
using Mogo.Task;

namespace Mogo.Game
{
public partial class EntityParent
{
public ActorParent Actor { get; set; }
}
}

\client\Assets\Scripts\GameLogic\Entities\EntityMonster.cs 服务器端控制的怪物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

using Mogo.FSM;
using Mogo.Util;
using Mogo.GameData;
using System;
using System.Reflection;

namespace Mogo.Game
{
public class EntityMonster : EntityParent
{
public readonly static int Goddess = 5010;
public readonly static int Tower1 = 42101;
public readonly static int Tower2 = 42201;

private int m_model;

private int m_clientTrapId;

private UInt32 m_monsterId;

public override void CreateModel()
{
if (clientTrapId == 0)
{
CreateActualModel();
}
else
{
LoggerHelper.Debug("CreateBuildingModel");

IsGolem = true;

attachBuildingTime = TimerHeap.AddTimer(500, 0, AttachBuildingModel);
}
}

// 附加建筑模型
private void AttachBuildingModel()
{
// 所有含有GearParent及其子类组件的对象
GearParent[] gears = (GearParent[])GameObject.FindObjectsOfType(typeof(GearParent));

// 返回数组,需要遍历
foreach (GearParent gear in gears)
{
if (gear.ID == (uint)clientTrapId)
{
gear.gameObject.tag = "Monster";

LoggerHelper.Debug("CreateBuildingModel Position: " + gear.transform.position);

animator = gear.gameObject.GetComponent<Animator>();

// ActorMonster是继承MonoBehavior的
ActorMonster ap = gear.gameObject.GetComponent<ActorMonster>();

if (ap == null)
ap = gear.gameObject.AddComponent<ActorMonster>();

ap.theEntity = this;
this.Actor = ap;

golem = gear.gameObject.GetComponentInChildren<GolemAnimation>();
golemFx = gear.gameObject.GetComponentInChildren<GolemFx>();

if (golem != null)
golem.Activate()
if (golemFx != null)
golemFx.Activate();

BornHandler();
}
}
}

//
public override void CreateDefaultModel()
{
AvatarModelData data = AvatarModelData.dataMap[999];
LoggerHelper.Debug("monster create:" + ID + ",name:" + data.prefabName);
GameObject go = AssetCacheMgr.GetLocalInstance(data.prefabName) as GameObject;

go.transform.localScale = scale;
go.tag = "Monster";
motor = go.AddComponent<MogoMotorServer>();
animator = go.GetComponent<Animator>();

ActorMonster ap = go.AddComponent<ActorMonster>();
ap.theEntity = this;
this.Actor = ap;
UpdatePosition();
base.CreateModel();
}
}
}

GameLogic/BattleSystem/BattleManager.cs 战斗管理系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

using Mogo.Util;
using Mogo.FSM;
using Mogo.GameData;

namespace Mogo.Game
{
// 属性
static class BattleAttr
{
static public string ATTACK = "atk"; // 伤害
static public string DEFENSE = "def"; // 防御
static public string FIGHT_FORCE = "fightForce"; // 攻击力
static public string HIT = "hit"; // 命中
static public string HEALTH = "curHp"; // 生命
static public string CRIT = "crit"; //暴击
static public string ANTI_CRIT = "antiCrit"; // 抗暴击
static public string TRUE_STRIKE = "trueStrike"; // 破击
static public string ANTI_TRUE_STRIKE = "antiTrueStrike"; // 抗破击
static public string ANTI_DEFENSE = "antiDefense"; // 穿刺
static public string CRIT_EXTRA_ATTACK = "critExtraAttack"; // 爆伤加成
static public string PVP_ADDITION = "pvpAddition"; // PVP强度
static public string PVP_ANTI = "pvpAnti"; // PVE强度
static public string CD_REDUCE = "cdReduce"; // CD减少
static public string SPEED_ADD_RATE = "speedAddRate"; // 速度增加

static public string EARTH_DAMAGE = "earthDamage"; // 土元素伤害
static public string AIR_DAMAGE = "airDamage"; // 风元素伤害
static public string WATER_DAMAGE = "waterDamage"; // 水元素伤害
static public string FIRE_DAMAGE = "fireDamage"; // 火元素伤害
static public string ALL_ELEMENTS_DAMAGE = "allElementsDamage"; // 全元素伤害

static public string EARTH_DEFENSE = "earthDefense"; // 土元素抗性
static public string AIR_DEFENSE = "airDefense"; // 风元素抗性
static public string WATER_DEFENSE = "waterDefense"; // 水元素抗性
static public string FIRE_DEFENSE = "fireDefense"; // 火元素抗性
static public string ALL_ELEMENTS_DEFENSE = "allElementsDefense"; // 全元素抗性
}

public class BattleManager
{
protected EntityParent theOwner; // 实体父类
protected SkillManager skillManager; // 技能管理

private int currentSpellID = 0; // 当前正在使用的技能配置数据(未使用技能时为0)
private int currentHitSpellID = 0; // 受击技能

public int CurrentSpellID
{
get { return currentSpellID; }
set { currentSpellID = value; }
}

public int CurrentHitSpellID
{
get { return currentHitSpellID; }
set { currentHitSpellID = value; }
}

public BattleManager(EntityParent _owner, SkillManager _skillManager)
{
theOwner = _owner;
skillManager = _skillManager;
// 添加监听
theOwner.AddUniqEventListener<int>(Events.FSMMotionEvent.OnPrepareEnd, OnPrepareEnd);

theOwner.AddUniqEventListener<int>(Events.FSMMotionEvent.OnAttackingEnd, OnAttackingEnd);

theOwner.AddUniqEventListener(Events.FSMMotionEvent.OnRollEnd, OnRollEnd);

EventDispatcher.AddEventListener<int, uint, uint, List<int>>(Events.FSMMotionEvent.OnHit, OnHit);
}

// 析构函数,移除监听的各种事件
virtual public void Clean()
{
theOwner.RemoveUniqEventListener<int>(Events.FSMMotionEvent.OnPrepareEnd, OnPrepareEnd);
theOwner.RemoveUniqEventListener<int>(Events.FSMMotionEvent.OnAttackingEnd, OnAttackingEnd);
theOwner.RemoveUniqEventListener<int>(Events.FSMMotionEvent.OnHitAnimEnd, OnHitAnimEnd);
theOwner.RemoveUniqEventListener(Events.FSMMotionEvent.OnRollEnd, OnRollEnd);

EventDispatcher.RemoveEventListener<int, uint, uint, List<int>>(Events.FSMMotionEvent.OnHit, OnHit);
}


}
}

GameLogic/BattleSystem/PlayerBattleManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

using Mogo.Util;
using Mogo.FSM;
using Mogo.GameData;

namespace Mogo.Game
{
public class PlayerBattleManager : BattleManager
{
private float lastAttackTime = 0.0f;
private PlayerSkillManager skillManager;

private List<int> preCmds = new List<int>();
private uint timeOutId = 0;

public PlayerBattleManager(EntityParent _owner, SkillManager _skillManager) : base(_owner, _skillManager)
{
skillManager = _skillManager;
}

// 析构函数,移除监听的各种事件
public override void Clean()
{
base.Clean();
}

// 当受击的时候出来
public override void OnHit(int _spellID, uint _attackerID, uint woundId, List<int> harm)
{
if (MogoWorld.inCity ||
theOwner.ID != woundId ||
!theOwner.canBeHit ||
theOwner.ID == _attackerID ||
((EntityMyself)theOwner).deathFlag > 0 ||
theOwner.charging)
{
return;
}
}
}
}

GameLogic\SfxSystem\SfxHandler.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:HandleCur
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:动作事件处理器。
//----------------------------------------------------------------*/
#endregion

using UnityEngine;
using System.Collections;
using Mogo.GameData;
using System.Collections.Generic;
using Mogo.Util;
using System.IO;
using System;

/// <summary>
/// 动作事件处理器。
/// </summary>
public class SfxHandler : MonoBehaviour
{
private Dictionary<string, Transform> m_locationDic = new Dictionary<string, Transform>();
private Dictionary<int, Dictionary<int, GameObject>> m_fxDic = new Dictionary<int, Dictionary<int, GameObject>>();
private Dictionary<int, List<int>> m_groupFXList = new Dictionary<int, List<int>>();

// MeleeWeaponTrail是一个武器轨迹插件
// Melee近战
private Dictionary<string, MeleeWeaponTrail> m_weaponTrailsDic = new Dictionary<string, MeleeWeaponTrail>();

private static Dictionary<string, Material> m_weaponTrailMaterial = new Dictionary<string, Material>();
private static Dictionary<string, AnimationClip> m_animationClip = new Dictionary<string, AnimationClip>();
private Renderer[] m_renderer;
private Material[] m_mat;

// HashSet<T>类,主要被设计用来存储集合,做高性能集运算,例如两个集合求交集、并集、差集等。
// 这个集合类包含不重复项的无序列表。
// 特性:
// 1.HasSet中的值不能重复
// 2.HashSet中的值没有顺序
// 3.HashSet的容量按需自动添加

// 适用场景:
// 高性能检索。Contains的方法性能好。HashSet<T>的Contains方法复杂度是O(1),List<T>的Contains方法复杂度是O(n),后者数据量越大速度越慢,而HashSet<T>不受数据量的影响。
// HashSet<T>集合会对加入的数据distinct,如果之前已经存在则就不会Add进去了。
private static HashSet<string> m_loadedFX = new HashSet<string>();

// 记录SlotCueHandler
SlotCueHandler slotCueHandler;

void Awake()
{
slotCueHandler = gameObject.GetComponent<slotCueHandler>();
GetMaterials();
EventDispatcher.AddEventListener<GameObject>(Events.OtherEvent.OnChangeWeapon, OnChangeWeapon);
EventDispatcher.AddEventListener(ActorParent.ON_EQUIP_DONE, OnEquitDone);
}

void OnDestroy()
{
EventDispatcher.RemoveEventListener<GameObject>(Events.OtherEvent.OnChangeWeapon, OnChangeWeapon);
EventDispatcher.RemoveEventListener(ActorParent.ON_EQUIP_DONE, OnEquitDone);
Clear();
}

public static void AddloadedFX(String fxResourceName)
{
m_loadedFX.Add(fxResourceName);
}

public static void UnloadAllFXs()
{
foreach(var item in m_loadedFX)
{
AssetCacheMgr.ReleaseResourceImmediate(item);
}
m_loadedFX.Clear();
}

/// <summary>
/// 发射弓箭或火球等
/// </summary>
/// <param name="shootSfxId">飞行物</param>
/// <param name="boomSfxId">碰撞后特效,-1代表无</param>
/// <param name="target">目标</param>
/// <param name="speed">速度</param>
/// <param name="distance">如果目标为null时,到前方一定距离后就消失</param>
public void Shoot(FXData fx, Transform target, float speed = 10, float distance = 30)
{
PlayFX(fx.id, fx, (go, guid) =>
{
var bullet = go.AddComponent<ActorBullect>();
bullet.OnDestroy = () =>
{
RemoveFx(fx.id, guid);
};
Vector3 targetPosition = Vector3.zero;

if (target == null)
targetPosition = go.transform.position + transform.forward * distance;

bullet.Setup(target, speed, targetPosition);
});
}

/// <summary>
/// 播放特效
/// </summary>
/// <param name="id"></param>
/// <param name="fx"></param>
/// <param name="action"></param>
/// <param name="bone_path">绑特效的骨骼,默认为空(即使用xml里面指定的骨骼)</param>
private void PlayFX(int id, FXData fx, Action<GameObject, int> action = null, string bone_path = "")
{

}

private void GetMaterials()
{
var renders = new List<Renderer>();
// true表示游戏对象下的子物体激活的没激活的都会被拿到,包括游戏对象本身
// includeInactive
var smr = GetComponentsInChildren<SkinnedMeshRenderer>(true);
var mr = GetComponentsInChildren<MeshRenderer>(true);

// 添加实现了接口IEnumerable<T>的一个泛型集合的所有元素到指定泛型集合末尾
// List<int> list1 = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// List<int> list2 = new List<int>() { 11,12};
// list1.AddRange(list2);
// Add 是每次将单个元素添加到集合里面。但是AddRange可以一次性添加多个元素到集合里面
renders.AddRange(smr);
renders.AddRange(mr);

if (renders.Count != 0)
{
m_renderer = renders.ToArray();
m_mat = new Material[m_renderer.Length];

for(int i = 0; i < m_renderer.Length; i++)
{
m_mat[i] = m_renderer[i].material; // Renderer.material
}
}
else
{
m_renderer = null;
m_mat = null;
}
}

private void OnChangeWeapon(GameObject go)
{
if (this && go)
{
var wt = go.GetComponent<MeleeWeaponTrail>();
if (wt)
{
m_weaponTrailsDic[wt.transform.parent.name] = wt;
}
else
{
LoggerHelper.Debug("No MeleeWeaponTrail: " + go.name);
if (m_weaponTrailsDic.ContainsKey(wt.transform.parent.name))
m_weaponTrailsDic.Remove(wt.transform.parent.name);
}
}
}

private void OnEquitDone()
{
if (this)
GetMaterials();
}

public void Clear()
{
RemoveAllFX();
m_mat = null;
m_renderer = null;
m_weaponTrailsDic.Clear();
m_locationDic.Clear();
}

public void RemoveAllFX()
{
List<int> list = new List<int>();
foreach(var fxs in m_fxDic)
{
list.Add(fxs.Key);
}
foreach (var item in list)
{
RemoveFXs(item);
}
m_fxDic.Clear();
StopShaderFX();
}

public void RemoveFXs(int id)
{
if (id == 0)
return;
var fx = FXData.dataMap.Get(id);
RemoveFXs(id, fx.group);
if (id == currentShaderFx)
StopShaderFX();
}

public void RemoveFXS(int id, int group)
{
if (m_fxDic.ContainsKey(id))
{
/*
KeyValuePair 和 Dictionary 的关系
1、KeyValuePair 
    a、KeyValuePair是一个结构体(struct);
    b、KeyValuePair只包含一个Key、Value的键值对。
2、Dictionary 
    a、Dictionary可以简单的看作是KeyValuePair 的集合;
    b、Dictionary可以包含多个Key、Value的键值对。
*/
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
foreach (var item in m_fxDic[id])
{
list.Add(new KeyValuePair<int, int>(id, item.Key));
}
foreach (var item in list)
{
RemoveFX(item.Key, item.Value, group);
}
m_fxDic.Remove(id);
}
}

public void RemoveFX(int id, int guid)
{
if (m_fxDic.ContainsKey(id) && m_fxDic[id].ContainsKey(guid))
{
GameObject.Destroy(m_fxDic[id][guid]);
//AssetCacheMgr.ReleaseInstance(guid);
m_fxDic[id].Remove(guid);
}
}

public void RemoveFXByGroup(int group)
{
if (m_groupFXList.ContainsKey(group))
{
var list = m_groupFXList[group].ToArray();

foreach (var item in list)
{
RemoveFXs(item, group);
}
}
}

public void StopShaderFX()
{
//StopCoroutine("UpdateShader");
updatingShader = false;
SetMatShader(orgShader);
currentShaderFx = 0;
}

private void SetMatShader(Shader shader)
{
if (m_mat != null && shader)
foreach (var item in m_mat)
{
//LoggerHelper.Error("SetMatShader item: " + item.name);
item.shader = shader; // Material.shader
}
}
}

\client\Assets\Resources\data\xml\SkillBuff.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<skill_buff>
<id_i>1</id_i>
<name_s>11001</name_s>
<saveDB_i>0</saveDB_i>
<show_i>0</show_i>
<removeMode_i>1</removeMode_i>
<totalTime_i>5000</totalTime_i>
<activeSkill_m>0:9000,1000:9000,2000:9000</activeSkill_m>
<sfx_i>101163</sfx_i>
</skill_buff>

<skill_buff>
<id_i>2</id_i>
<name_s>11002</name_s>
<saveDB_i>0</saveDB_i>
<show_i>0</show_i>
<removeMode_i>1</removeMode_i>
<totalTime_i>8000</totalTime_i>
<activeSkill_m>0:9000,1000:9000,2000:9000,3000:9000,4000:9000</activeSkill_m>
<sfx_i>101163</sfx_i>
</skill_buff>
</root>

\client\MogoSolution\GameData\SkillBuffData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,爱游
//
// 模块名:buff表
// 创建者:莫卓豪
// 修改者列表:
// 创建日期:2013-7-10
// 模块描述:
//----------------------------------------------------------------*/

using System.Collections;
using System.Collections.Generic;

namespace Mogo.GameData
{
public class SkillBuffData : GameData<SkillBuffData>
{
public int name { get; protected set; }
public int show { get; protected set; }
public int showPriority { get; protected set; }
public int removeMode { get; protected set; }
public int totalTime { get; protected set; }
public List<int> excludeBuff { get; protected set; }
public List<int> replaceBuff { get; protected set; }
public List<int> appendState { get; protected set; }
public Dictionary<int, int> activeSkill { get; protected set; }
public Dictionary<string, int> attrEffect { get; protected set; }
public int sfx { get; protected set; }
public int notifyEvent { get; protected set; }
public int vipLevel { get; protected set; }
public int tips { get; protected set; }

static public readonly string fileName = "xml/SkillBuff";
//static public Dictionary<int, SkillBuffData> dataMap { get; set; }

public static string GetName(int id)
{
string rst = "no name";
if (!dataMap.ContainsKey(id))
{
return rst;
}
if (!LanguageData.dataMap.ContainsKey(dataMap[id].name))
{
return rst;
}
return LanguageData.GetContent(dataMap[id].name);
}
}
}

\client\Assets\Scripts\GameLogic\BattleSystem\BuffManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using Mogo.Game;
using Mogo.GameData;
using Mogo.Util;

public class BuffManager
{
public class BuffManager
{
public class ClientBuff
{
public int id;
public int totalTime;
public int startTime;

public ClientBuff(SkillBuffData cfg)
{
id = cfg.id;
totalTime = cfg.totalTime;
startTime = (int)(Time.realtimeSinceStartup * 1000);
}
}

private uint timer = 0;
private Dictionary<int, ClientBuff> clientBuffs;

private EntityParent m_theOwner;
public BuffManager(EntityParent theOwner)
{
m_theOwner = theOwner;
clientBuffs = new Dictionary<int, ClientBuff>();
EventDispatcher.AddEventListener(Events.OtherEvent.SecondPast, CountDownSkillBuffTime);
timer = TimerHeap.AddTimer(500, 100, UpdateBuff);
}

private bool m_hasGotLoginBuff = false;
public bool HasGotLoginBuff
{
get
{
return m_hasGotLoginBuff;
}
set
{
m_hasGotLoginBuff = value;
}
}

struct BuffData
{
public int buffId;
public UInt32 lastTime;
}

List<BuffData> m_skillBuffDataList = new List<BuffData>();

public void Clean()
{
TimerHeap.DelTimer(timer);
EventDispatcher.RemoveEventListener(Events.OtherEvent.SecondPast, CountDownSkillBuffTime);
clientBuffs.Clear();
m_skillBuffDataList.Clear();
}


}
}

Gears

暗黑战神\client\Assets\Scripts\Gears\GearParent\GearParent.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,公司名
//
// 模块名:GearParent
// 创建者:
// 修改者列表:
// 创建日期:
// 最后修改日期:
// 模块描述:机关父类,每个机关功能通过一个或多个机关子类组合实现
// 代码版本:发布版V3.0
//----------------------------------------------------------------*/

using UnityEngine;
using System.Collections;
using Mogo.Util;

public class GearParent : MonoBehaviour
{
#region 静态常量

public static readonly string MogoPlayerTag = "Player"; // 角色的tag
public static readonly byte EnableByte = 8;
public static readonly byte DisableByte = 4;
public static readonly byte StateOneByte = 2;
public static readonly byte StateTwoByte = 1;

#endregion

#region 动态变量

public int defaultID = 0;
public uint ID = 0;
public string gearType { get; protected set; } // 机关类型名,每个子类维护自己的类型名

public bool triggleEnable = false; // 是否可触发
public bool stateOne = false;

#endregion

#region 创建与销毁

void Start()
{
gearType = "GearParent";
ID = (uint)defaultID;
triggleEnable = true;
stateOne = true;

AddListeners();
}

// 销毁的时候去掉监听
void OnDestroy()
{
RemoveListeners();
}

#endregion

#region 事件监听

public virtual void AddListeners()
{
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearEnable, SetGearEnable);
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearDisable, SetGearDisable);
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearStateOne, SetGearStateOne);
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearStateTwo, SetGearStateTwo);

EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearEventEnable, SetGearEventEnable);
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearEventDisable, SetGearEventDisable);
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearEventStateOne, SetGearEventStateOne);
EventDispatcher.AddEventListener<uint>(Events.GearEvent.SetGearEventStateTwo, SetGearEventStateTwo);
}

public virtual void RemoveListeners()
{
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearEnable, SetGearEnable);
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearDisable, SetGearDisable);
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearStateOne, SetGearStateOne);
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearStateTwo, SetGearStateTwo);

EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearEventEnable, SetGearEventEnable);
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearEventDisable, SetGearEventDisable);
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearEventStateOne, SetGearEventStateOne);
EventDispatcher.RemoveEventListener<uint>(Events.GearEvent.SetGearEventStateTwo, SetGearEventStateTwo);
}

#endregion

#region 机关事件

protected virtual void SetGearEventEnable(uint enableID)
{
if (enableID == ID)
{
triggleEnable = true;
EventDispatcher.TriggerEvent(Events.GearEvent.UploadAllGear);
}
}

protected virtual void SetGearEventDisable(uint disableID)
{
if (disableID == ID)
{
triggleEnable = false;
EventDispatcher.TriggerEvent(Events.GearEvent.UploadAllGear);
}
}

protected virtual void SetGearEventStateOne(uint stateOneID)
{
if (stateOneID == ID)
{
stateOne = true;
EventDispatcher.TriggerEvent(Events.GearEvent.UploadAllGear);
}
}

protected virtual void SetGearEventStateTwo(uint stateTwoID)
{
if (stateTwoID == ID)
{
stateOne = false;
EventDispatcher.TriggerEvent(Events.GearEvent.UploadAllGear);
}
}

#endregion

#region 断线重连

protected virtual void SetGearEnable(uint enableID)
{
if (enableID == ID)
{
triggleEnable = true;
}
}

protected virtual void SetGearDisable(uint disableID)
{
if (disableID == ID)
{
triggleEnable = false;
}
}

protected virtual void SetGearStateOne(uint stateOneID)
{
if (stateOneID == ID)
{
stateOne = true;
}
}

protected virtual void SetGearStateTwo(uint stateTwoID)
{
if (stateTwoID == ID)
{
stateOne = false;
}
}

#endregion

}

暗黑战神\client\Assets\Scripts\Gears\DropRock\DropRock.cs 丢石头机关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using UnityEngine;
using System.Collections;
using Mogo.Util;
using System.Collections.Generic;
using Mogo.Game;

public class DropRock : GearParent // 所有机关都继承自GearParent机关父类
{
public bool canAttackDummy = false; // 是否可以攻击怪物

public float radius; // 半径
public float percentage; // 进度
public int damageMin; // 最小伤害
public int damageMax; // 最大伤害

protected SfxHandler sfxHandler { get; set; } // 特效交给专门的类来处理
protected uint timerID;

void Start()
{
gearType = "DropRock"; // 每个子类维护自己的类型名
gameObject.AddComponent<Rigidbody>(); // 添加刚体
triggleEnable = true;

sfxHandler = gameObject.AddComponent<SfxHandler>(); // 添加特效处理脚本
timerID = uint.MaxValue;
}

void OnDestroy()
{
TimerHeap.DelTimer(timerID);
}

// 触发
void OnTriggerEnter(Collider other)
{
if(triggleEnable)
{
if(other.gameObject.layer == 9)
{
MogoMainCamera.Instance.Shake(5, 0.1f);
sfxHandler.HandlerFx(500102);

SetDamage(MogoUtils.GetEntitiesInRange(transform, radius));

triggleEnable = false;

if (timerID == uint.MaxValue)
timerID = TimerHeap.AddTimer(5000, 0, RockDestroy);
}
}
}

private void RockDestroy()
{
sfxHandler.RemoveFXs(500102);
// sfxHandler.RemoveFXs(6011);
Destroy(this.gameObject);
}

private void SetDamage(List<List<uint>> entities)
{
if (entities.Count != 4)
return;

List<uint> dummyList = entities[0];
List<uint> playerList = entities[2];

if (canAttackDummy)
{
foreach (uint id in dummyList)
{
EventDispatcher.TriggerEvent(Events.GearEvent.Damage, id, 9003, (int)2, CalcDamage(MogoWorld.GetEntity(id) as EntityParent));
}
}

foreach (uint id in playerList)
{
if (id == MogoWorld.thePlayer.ID)
{
EventDispatcher.TriggerEvent(Events.GearEvent.Damage, id, 9003, (int)2, CalcDamage(MogoWorld.thePlayer as EntityParent));
break;
}
}
}

private int CalcDamage(EntityParent entity)
{
return (int)(entity.hp * percentage + RandomHelper.GetRandomInt(damageMin, damageMax));
}
}

MogoWorld

暗黑战神\client\Assets\Scripts\GameLogic\MogoWorld.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:MogoWorld
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:游戏全局数据管理器
//----------------------------------------------------------------*/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Mogo.Util;
using Mogo.RPC;
using Mogo.Game;
using Mogo.GameData;
using System.Text;
using System.Security.Cryptography;
using System.Reflection;
using Mogo.FSM;

public class MogoWorld
{
#region Properties
static public GlobalData globalSetting;

#endregion
}

EntityMyself

\client\Assets\Scripts\GameLogic\Entities\EntityMyself.cs 玩家控制的角色的逻辑控制类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:EntityMyself
// 创建者:
// 修改者列表:
// 创建日期:2
// 模块描述:角色控制对像
//----------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using UnityEngine;
using Mogo.Task;
using Mogo.Mission;
using Mogo.GameData;
using Mogo.Util;
using Mogo.RPC;
using Mogo.FSM;

using Mogo.AI;
using Mogo.AI.BT;

public enum VipRealStateEnum
{
DAILY_GOLD_METALLURGY_TIMES = 1, // 每日已炼金次数
DAILY_RUNE_WISH_TIMES = 2, // 符文已许愿次数
DAILY_ENERGY_BUY_TIMES = 3, // 体力已购买次数
DAILY_EXTRA_CHALLENGE_TIMES = 4, // 每日额外挑战次数
DAILY_HARD_MOD_RESET_TIMES = 5, // 困难副本进入已重置次数
DAILY_RAID_SWEEP_TIMES = 6, // 剧情关卡已扫荡次数
DAILY_TOWER_SWEEP_TIMES = 7, // 试炼之塔已扫荡次数
DAILY_TIME_STAMP = 8, // 每日玩家时间戳记录
DAILY_ITEM_CAN_BUY_ENTER_SDTIMES = 9, // 圣域守卫战额外购买次数
DAILY_MISSION_TIMES = 10,// 每天副本的进入次数
}

namespace Mogo.Game
{
public partial class EntityMyself : EntityPlayer
{
override public void CreateActualModel()
{
isCreatingModel = true;

// Get()是什么?
AvatarModelData data = AvatarModelData.dataMap.Get((int)vocation);

}
}
}

场景管理

场景自带了fog,然后light是disable的,没有摄像机。

\client\Assets\Resources\data\xml\GlobalData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<root>
<record>
<id>0</id>
<chooseServerScene>10001</chooseServerScene>
<loginScene>10002</loginScene>
<chooseCharaterScene>10003</chooseCharaterScene>
<homeScene>10004</homeScene>
<loginUI>MogoMainUI.prefab</loginUI>
<chooseServerUI>MogoMainUI.prefab</chooseServerUI>
<chooseCharaterUI>MogoMainUI.prefab</chooseCharaterUI>
<homeUI>MogoMainUI.prefab</homeUI>
<battleUI>MogoMainUI.prefab</battleUI>
<mainCamera>Main_Camera.prefab</mainCamera>
<tower_all_sweep_vip_level_i>5</tower_all_sweep_vip_level_i>
</record>
</root>

\client\MogoSolution\GameData\GlobalData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Mogo.GameData
{
public class GlobalData : GameData<GlobalData>
{
public Int32 loginScene { get; protected set; }
public Int32 chooseServerScene { get; protected set; }
public Int32 chooseCharaterScene { get; protected set; }
public Int32 homeScene { get; protected set; }
public String loginUI { get; protected set; }
public String chooseServerUI { get; protected set; }
public String chooseCharaterUI { get; protected set; }
public String homeUI { get; protected set; }
public String battleUI { get; protected set; }
public String mainCamera { get; protected set; }
public int tower_all_sweep_vip_level { get; protected set; }
public static readonly string fileName = "xml/GlobalData";
//public static Dictionary<int, GlobalData> dataMap { get; set; }
}
}

client\Assets\Resources\data\xml\map_setting.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<map>
<id_i>10003</id_i>
<type_i>0</type_i>
<enterX_i>743</enterX_i>
<enterY_i>975</enterY_i>
<maxPlayerNum_i>10</maxPlayerNum_i>
<modelName>10003_ChooseCharactor.prefab:1</modelName>
<cameraColor>29, 31, 63, 255</cameraColor>
<cameraFar>400</cameraFar>
<mapname_s>test</mapname_s>
<width>100</width>
<height>100</height>
<fog>True</fog>
<fogColor>29, 31, 63, 255</fogColor>
<fogMode>1</fogMode>
<linearFogStart>4</linearFogStart>
<linearFogEnd>50</linearFogEnd>
<ambientLight>175,175,175,255</ambientLight>
<sceneName>10003_ChooseCharactor</sceneName>
<trapID_i>10003</trapID_i>
<backgroundMusic>45</backgroundMusic>
<lightmap>10003_Lightmap.exr</lightmap>
</map>
</root>

\client\MogoSolution\GameData\MapData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Mogo.GameData
{
public class MapData : GameData<MapData>
{
public MapType type { get; protected set; }
public short enterX { get; protected set; }
public short enterY { get; protected set; }
public String lightmap { get; protected set; }
public String lightProbes { get; protected set; }
public Dictionary<String, bool> modelName { get; protected set; }
public Color cameraColor { get; protected set; }
public float cameraFar { get; protected set; }
public bool fog { get; protected set; }
public Color fogColor { get; protected set; }
public FogMode fogMode { get; protected set; }
public float linearFogStart { get; protected set; }
public float linearFogEnd { get; protected set; }
public Color ambientLight { get; protected set; }
public Dictionary<int, Vector3> monstors { get; protected set; }
public List<int> layerList { get; protected set; }
public List<float> distanceList { get; protected set; }
public String sceneName { get; protected set; }
public LightType characherLight { get; protected set; }

public List<int> npcList { get; protected set; }
public int trapID { get; protected set; }
public List<int> backgroundMusic { get; protected set; }
public static readonly string fileName = "xml/map_setting";
//public static Dictionary<int, MapData> dataMap { get; set; }



public MapData()
{
sceneName = "InstanceScene";
enterX = -200;
enterY = -200;
}

public static bool IsSceneShowDeadTip(ushort sceneId)
{
if (dataMap == null)
return false;

if (!dataMap.ContainsKey(sceneId))
return false;

if (dataMap[sceneId].type == MapType.Special
|| dataMap[sceneId].type == MapType.ARENA)
return true;

return false;
}
}

public enum LightType : byte
{
/// <summary>
/// 不加光
/// </summary>
None = 0,
/// <summary>
/// 普通光
/// </summary>
Normal = 1
}

public enum MapType : byte
{
/// <summary>
/// 普通地图
/// </summary>
Normal = 0,
/// <summary>
/// 副本地图
/// </summary>
Special = 1,
/// <summary>
/// 试炼之塔
/// </summary>
ClimbTower = 2,
/// <summary>
/// 多人非组队
/// </summary>
MULTIPLAYER = 3,
/// <summary>
/// 世界boss
/// </summary>
WORLDBOSS = 4,
/// <summary>
/// 湮灭之门
/// </summary>
BURY = 5,
/// <summary>
/// 竞技场地图
/// </summary>
ARENA = 6,
/// <summary>
/// 塔防地图
/// </summary>
TOWERDEFENCE = 8,

/// <summary>
/// 袭击地图
/// </summary>
ASSAULT = 9,

/// <summary>
/// PVP地图
/// </summary>
OCCUPY_TOWER = 11,

/// <summary>
/// 迷雾深渊地图
/// </summary>
FOGGYABYSS = 12,
}
}

\client\Assets\Scripts\GameLogic\ScenesManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#region 模块信息
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:ScenesManager
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:场景管理器
//----------------------------------------------------------------*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using UnityEngine;
using Mogo.GameData;
using Mogo.Util;
using System.Text.RegularExpressions;
using HMF;
using System.Collections;

namespace Mogo.Game
{
public class ScenesManager : IEventManager
{
private Int32 m_lastScene = -1;
private string m_lastSceneResourceName = String.Empty;

private MapData m_currentMap;

public void AddListeners()
{

}

public void RemoveListeners()
{

}

// 当前场景的对象
private List<UnityEngine.Object> m_sceneObjects;

public ScenesManager()
{
m_sceneObjects = new List<UnityEngine.Object>();
}

/// <summary>
/// 加载摄像机。
/// </summary>
/// <param name="missionID"></param>
/// <param name="data">场景地图数据实体对象</param>
/// <param name="loaded">加载完成后的回调</param>
private void LoadCamera(int missionID, MapData data, Action loaded = null)
{
AssetCacheMgr.GetInstanceAutoRelease(
MogoWorld.globalSetting.mainCamera,
(prefab, id, go) =>
{
if (m_currentMap != null)
{
go.name = Mogo.Util.Utils.GetFileNameWithoutExtention(MogoWorld.globalSetting.mainCamera);

var obj = go as GameObject;
obj.AddComponent<MogoMainCamera>();

Camera camera = obj.GetComponent<Camera>();

UnityEngine.Object.Destroy(camera.GetComponent<AudioListener>());

camera.backgroundColor = m_currentMap.cameraColor;
// 远剪切平面距离。
camera.farClipPlane = m_currentMap.cameraFar;

if (data != null && data.layerList != null && data.layerList.Count != 0 && data.distanceList != null && data.distanceList.Count != 0 && data.layerList.Count == data.distanceList.Count )
{
var cull = (go as GameObject).AddComponent<MogoCameraCullByLayer>();

cull.LayerList = data.layerList;
cull.DistanceList = data.distanceList;
}

EventDispatcher.TriggerEvent(SettingEvent.BuildSoundEnvironment, missionID);
}
if (loaded != null)
loaded();
TimerHeap.AddTimer(500, 0, MogoMessageBox.HideLoading);
}
);
}

/// <summary>
/// 加载场景。
/// </summary>
/// <param name="data">场景地图数据实体对象</param>
/// <param name="loaded">加载结束,参数为是否加载场景</param>
/// <param name="sceneName">场景名称</param>
/// <param name="process">传入UI 加载进度的函数</param>
private void LoadScene(MapData data, Action<Boolean> loaded, string sceneName, Action<int> process = null)
{
// try catch避免崩溃
try
{
// 读取和加载是两件事情
// 定义场景文件读取完毕后的处理函数
Action sceneWasLoaded = () =>
{
// 定义场景文件加载完毕后处理
Action LevelWasLoaded = () =>
{
LoggerHelper.Debug("LevelWasLoaded: " + sceneName);
// 找不到对应的prefab
if (data.modelName.Count == 0)
if (loaded != null)
// 执行加载完成委托
loaded(true);

LoggerHelper.Debug("modelName Count: " + data.modelName.Count);
if (process != null)
process(20);

// 处理prefab
// modelName是字典Dictionary<String, bool>
// 获得KeyList
var models = data.modelName.Keys.ToList();
for(int i = 0; i < models.Count; i ++)
{
var currentOrder = i;
LoggerHelper.Debug("modelName order: " + currentOrder);

// 加载场景模型
// 参数:prefab名,string类型
// 资源实例加载完成回调Action<String, int, Object> loaded,所以go是个对象,可以给它的字段赋值
// progress回调 Action<float> progress
AssetCacheMgr.GetSceneInstance(models[currentOrder], (pref, id, go) =>
{
go.name = Util.GetFileNameWithoutExtention(models[currentOrder]);

if (data.modelName[models[currentOrder]])
// Unity可以勾选static来进行静态合批, 运行时也可以 StaticBatchingUtility.Combine()来进行合批。
StaticBatchingUtility.Combine(go as GameObject);
LoggerHelper.Debug("sceneLoaded: " + go.name);

m_sceneObjects.Add(go);

// currentOrder是List的下标,这儿判断是否是最后一个
if (currentOrder == data.modelName.Count -1)
{
AssetCacheMgr.UnloadAssetbundles(models.ToArray());
SwitchLightMapFog(data, loaded);
}
}, (process) =>
{
float cur = 60 * ((progress + currentOrder) / models.Count) + 20;
if (process != null)
process((int)(cur));
}
);
}
// 委托置空
Driver.Instance.LevelWasLoaded = null;
};

// ↑ LevelWasLoaded委托赋值完毕,判断然后执行
if (sceneName != "10002_Login")
{
Driver.Instance.LevelWasLoaded = () =>
{
Driver.Instance.StartCoroutine(
UnloadUnusedAssets(() =>
{
GC.Collect();
LevelWasLoaded();
}));
}
Application.LoadLevel(sceneName);
}
else
{
LevelWasLoaded();
}
LoggerHelper.Debug("LoadLevel: " + sceneName);
};

// ↑ sceneWasLoaded委托已经赋值,下面进行判断然后执行

if (SystemSwitch.ReleaseMode)
{
if (process != null)
process(5);

m_lastSceneResourceName = string.Concat(sceneName, ".unity");
AssetCacheMgr.GetResource(m_lastSceneResourceName,
(scene) => // Action<Object> loaded 资源对象加载完成回调
{
sceneLoaded();
},
(progress) =>
{
float cur = 15 * progress + 5;
if (process != null)
process((int)(cur));
}
);
}
else
sceneWasLoaded();
}
catch (Exception ex)
{
LoggerHelper.Except(ex);
}
}
}
}

AssetCacheMgr

\client\MogoSolution\LoaderLib\Utils\AssetCacheMgr.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using UnityEngine;
using System.Collections.Generic;
using System;
using Object = UnityEngine.Object;
using Mogo.Util;
using System.Collections;

// 必须实现接口的所有方法。
public interface ILoadAsset
{
void LoadInstance(string prefab, Action<String, int, Object loaded>);
void LoadInstance(string prefab, Action<String, int, Object> loaded, Action<float> progress);
Object SynLoadInstance(string prefab);
void LoadSceneInstance(string prefab, Action<String, int, Object> loaded, Action<float> progress);
void LoadAsset(string prefab, Action<Object> loaded);
void LoadAsset(string prefab, Action<Object> loaded, Action<float> progress);
void LoadUIAsset(string prefab, Action<Object> loaded, Action<float> progress);
void SecondLoadAsset(string prefab, Action<Object> loaded, Action<float> progress);
void SecondLoadUIAsset(string prefab, Action<Object> loaded, Action<float> progress);
Object SynLoadAsset(string prefab);
void LoadSceneAsset(string prefab, Action<Object> loaded);
Object LoadLocalInstance(string prefab);
Object LoadLocalAsset(string prefab);
void ReleaseLocalAsset(string prefab);
void Release(string prefab);
void Release(string prefab, Boolean releaseAsset);
void ReleaseUnusedAssets();
void SetPathMap();
void ForceClear();
void ClearLoadAssetTasks();
void UnloadAsset(string prefab);
}

public class AssetCacheMgr
{
private static Dictionary<int, string> m_gameObjectNameMapping = new Dictionary<int, string>();


private static ILoadAsset m_assetMgr;

public static ILoadAsset AssetMgr
{
get
{
return m_assetMgr;
}
set
{
m_assetMgr = value;
}
}

/// <summary>
/// 获取场景资源实例。
/// </summary>
/// <param name="resourceName">资源文件名(不带路径,带后缀)</param>
/// <param name="loaded">资源实例加载完成回调</param>
/// <param name=""></param>
public static void GetSceneInstance(string resourceName, Action<String, int, Object> loaded, Action<float> progress)
{
m_assetMgr.LoadSceneInstance(resourceName, (pref, guid, go) =>
{
if(guid == -1)
m_gameObjectNameMapping.Add(guid, resourceName);
if (loaded != null)
loaded(pref, guid, go);
}, progress);
}
}

任务与剧情

\client\Assets\Resources\data\xml\ChineseData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<record>
<id_i>100001</id_i>
<content_s>刺耳的龙吼,炽热的熔岩,这些都消失了!我又看见这久违的阳光了……</content_s>
</record>
<record>
<id_i>100002</id_i>
<content_s>神使{0},一年不见了。一年前,你还在屠龙的征途上。</content_s>
</record>
<record>
<id_i>100003</id_i>
<content_s>一年!多么不可思议!小精灵,请告诉我,为何我会被困在那无止境的噩梦里,请告诉我,混沌军团已经被主神大人击退了。</content_s>
</record>
</root>

client\MogoSolution\GameData\LanguageData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,爱游
//
// 模块名:LanguageData
// 创建者:Steven Yang
// 修改者列表:
// 创建日期:2013-2-7
// 模块描述:本地化语言信息数据模块
//----------------------------------------------------------------*/

using System.Collections;
using System.Collections.Generic;
using Mogo.Util;
using System;

namespace Mogo.GameData
{
public class LanguageData : GameData<LanguageData>
{
// Monster 数据
public string content { get; set; }

static public readonly string fileName = "xml/ChineseData";

public LanguageData()
{
content = string.Empty;
}

public string Format(params object[] args)
{
return string.Format(content, args);
}

public static string MONEY { get { return dataMap.Get(20002).content; }}

public static string EXP { get { return dataMap.Get(20003).content; } }

public static string DIAMOND { get { return dataMap.Get(20004).content; } }

public static string GetContent(int id)
{
if (dataMap.ContainsKey(id))
{
return dataMap.Get(id).content;
}
else
{
LoggerHelper.Error(String.Format("Language key {0:0} is not exist ", id));
return "***";
}
}

public static string GetContent(int id, params object[] args)
{
if (dataMap.ContainsKey(id))
{
return dataMap.Get(id).Format(args);
}
else
{
LoggerHelper.Error(String.Format("Language key {0:0} is not exist ", id));
return "***";
}
}

static public string GetPVPLevelName(int PVPLevel)
{
return dataMap.Get(3000 + PVPLevel).content;
}
}
}

client\Assets\Resources\data\xml\TaskData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<task>
<id_i>1</id_i>
<name_i>120000</name_i>
<nextId_i>2</nextId_i>
<level_i>1</level_i>
<npc_i>150000</npc_i>
<pathPoint_i>0</pathPoint_i>
<exp_i>0</exp_i>
<money_i>0</money_i>
<conditionType_i>0</conditionType_i>
<condition_l>150000</condition_l>
<text_m>100001:0,100002:150000,100003:0,100004:150000,100005:0,100006:150000,100007:150000</text_m>
<autoIcon_i>31310</autoIcon_i>
<isShowNPCTip_i>0</isShowNPCTip_i>
</task>
<task>
<id_i>2</id_i>
<name_i>120000</name_i>
<nextId_i>3</nextId_i>
<level_i>1</level_i>
<npc_i>150006</npc_i>
<pathPoint_i>150021</pathPoint_i>
<exp_i>0</exp_i>
<money_i>0</money_i>
<conditionType_i>0</conditionType_i>
<condition_l>150006</condition_l>
<text_m>100008:150006,100009:0,100010:150006,100011:150006,100012:150006</text_m>
<autoIcon_i>31310</autoIcon_i>
<isShowNPCTip_i>0</isShowNPCTip_i>
<tiptext_i>110001</tiptext_i>
<awards1_m>1312001:1,1711011:5</awards1_m>
<awards2_m>1332001:1,1711011:5</awards2_m>
<awards3_m>1372001:1,1711011:5</awards3_m>
<awards4_m>1362001:1,1711011:5</awards4_m>
</task>
</root>

\client\MogoSolution\GameData\TaskData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:MissionData
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:任务数据结构
//----------------------------------------------------------------*/

using System.Collections;
using System.Collections.Generic;

namespace Mogo.GameData
{
public class TaskData : GameData<TaskData>
{
public int type { get; protected set; }

public string name { get; protected set; }
public int nextId { get; protected set; }
public int level { get; protected set; }
public int npc { get; protected set; }

public Dictionary<int, int> text { get; protected set; }
public int tiptext { get; protected set; }

public int pathPoint { get; protected set; }

public int exp { get; protected set; }
public int money { get; protected set; }

public Dictionary<int, int> awards1 { get; protected set; }
public Dictionary<int, int> awards2 { get; protected set; }
public Dictionary<int, int> awards3 { get; protected set; }
public Dictionary<int, int> awards4 { get; protected set; }

public int time { get; protected set; }

public int conditionType { get; protected set; }
public List<int> condition { get; protected set; }

public int cameraAniId { get; protected set; }
public int avatarAniId { get; protected set; }

public int autoIcon { get; protected set; }
public int isShowNPCTip { get; protected set; }

static public readonly string fileName = "xml/TaskData";
//static public Dictionary<int, TaskData> dataMap { get; set; }
}
}

\client\Assets\Scripts\GameLogic\TaskSystem\TaskManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:MissionManager
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:任务管理器(逻辑层)
//----------------------------------------------------------------*/

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Mogo.GameData;
using Mogo.Util;
using Mogo.Game;
using System.Text.RegularExpressions;
using System;
using Mogo.FSM;

namespace Mogo.Task
{
public class TaskManager : IEventManager
{
public static readonly int SkyNPCID = 150000;
public static readonly int InstanceNPCID = 149999;

public enum DialogueRelationship
{
You = 1,
OlderToYounger = 2,
YoungerToOlder = 3
}

protected EntityMyself theOwner;

private int curNPCID = 0;

private TaskData m_playerCurrentTask;
public TaskData PlayerCurrentTask
{
get
{
return m_playerCurrentTask;
}
}

protected bool isTalking = false;
protected bool isFirstTask = false;

// 回城延时
public int delayHandleTaskID = -1;

public TaskManager(EntityMyself owner, int taskMain)
{
theOwner = owner;
InitAvatarTaskList(taskMain);

AddListeners();
}
/// <summary>
/// 初始化任务
/// </summary>
/// <param name=""></param>
private void InitAvatarTaskList(int taskMain)
{
if (taskMain < 1)
{
LoggerHelper.Debug("任务ID初始化不合法");
taskMain = 1;
}

if (taskMain == 1)
isFirstTask = true;

if (TaskData.dataMap.ContainsKey(taskMain))
m_playerCurrentTask = TaskData.dataMap[taskMain];


}


// 增加监听
public void AddListeners()
{
EventDispatcher.AddEventListener<int>(Events.TaskEvent.NPCInSight, OnNPCInSight);
...
}


// 移除监听
public void RemoveListeners()
{
EventDispatcher.RemoveEventListener<int>(Events.TaskEvent.NPCInSight, OnNPCInSight);
...
}

// 显示对话框
private void ShowTaskDialogue()
{
Mogo.Util.LoggerHelper.Debug("ShowTaskDialogue");
// m_playerCurrentTask是数据实体类
if (m_playerCurrentTask == null)
{
LoggerHelper.Debug("PlayerCurrentTask == null");
return;
}

// 没有谈话内容,直接谈话结束
if(m_playerCurrentTask.text == null)
{
OnTalkEnd();
return;
}

if (m_playerCurrentTask.text.Count == 0)
{
OnTalkEnd();
return;
}

List<string> allName = new List<string>();
List<string> allImg = new List<string>();
List<string> allText = new List<string>();

foreach(var item in m_playerCurrentTask.text)
{
int npcID = item.Value;
string imageName = "", npcName = "";

if (npcID == 0) // 等于0是自己的英雄
{
int imgID = 0;
switch(MogoWorld.thePlayer.vocation)
{
case Vocation.Warrior:
imageID = 310;
break;
case Vocation.Assassin:
imageID = 311;
break;
case Vocation.Archer:
imageID = 312;
break;
case Vocation.Mage:
imageID = 313;
break;
}
imageName = IconData.dataMap.Get(imageID).path;
npcName = MogoWorld.thePlayer.name;
}
else // 不为0就是npc
{
imageName = IconData.dataMap.Get(NPCData.dataMap[npcID].dialogBoxImage).path;
npcName = LanguageData.GetContent(NPCData.dataMap[npcID].name);
}

string[] tempText = LanguageData.GetContent(item.Key).Split('_');

for (int i = 0; i < tempText.Length; i++)
{
tempText[i] = FormatTaskText(tempText[i]);
}

int count = tempText.Length;

for (int i = 0; i < count; i++)
{
allName.Add(npcName);
allImg.Add(imageName);
allText.Add(tempText[i]);
}
}

ShowMogoTaskUI(allName.ToArray(), allImg.ToArray(), allText.ToArray(), MogoUIManager.Instance.m_NormalMainUI);
}
}
}

CG

\client\Assets\Resources\data\xml\StoryCG1.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<story>
<id>1</id>
<storyText>ResetControlStick</storyText>
</story>
<story>
<id>2</id>
<storyText>WhiteCamera 0,0 250</storyText>
</story>
<story>
<id>3</id>
<storyText>RemoveEntities</storyText>
</story>
<story>
<id>4</id>
<storyText>Sleep 500</storyText>
</story>
<story>
<id>5</id>
<storyText>SetPosition 0 23.38,-0.945,9.16</storyText>
</story>
<story>
<id>6</id>
<storyText>SetRotation 0 0,25,0</storyText>
</story>
<story>
<id>7</id>
<storyText>CreateModel 1 2016 3078 1501 0,245,0</storyText>
</story>
<story>
<id>8</id>
<storyText>Sleep 500</storyText>
</story>
<story>
<id>9</id>
<storyText>MoveCamera 1 5,5,62,0 5,5,62,0 4000</storyText>
</story>
<story>
<id>10</id>
<storyText>NormalCamera 0,0 1000</storyText>
</story>
<story>
<id>11</id>
<storyText>Sleep 500</storyText>
</story>
<story>
<id>12</id>
<storyText>ShowDialog 150018 399 105003</storyText>
</story>
<story>
<id>13</id>
<storyText>OpenUI</storyText>
</story>
<story>
<id>14</id>
<storyText>Wait 6</storyText>
</story>
<story>
<id>15</id>
<storyText>CloseUI</storyText>
</story>
<story>
<id>16</id>
<storyText>Sleep 500</storyText>
</story>
<story>
<id>17</id>
<storyText>PlaySfx 1 6015</storyText>
</story>
<story>
<id>18</id>
<storyText>PlayOneAction 1 17</storyText>
</story>
<story>
<id>19</id>
<storyText>CreateModel 2 1011 1177 1555 0,122,0</storyText>
</story>
<story>
<id>20</id>
<storyText>Sleep 1000</storyText>
</story>
<story>
<id>21</id>
<storyText>ZoomCamera 2 3 5,112 1500 1500</storyText>
</story>
<story>
<id>22</id>
<storyText>Sleep 1500</storyText>
</story>
<story>
<id>23</id>
<storyText>ShowDialog 150019 399 105004</storyText>
</story>
<story>
<id>24</id>
<storyText>OpenUI</storyText>
</story>
<story>
<id>25</id>
<storyText>Wait 6</storyText>
</story>
<story>
<id>26</id>
<storyText>CloseUI</storyText>
</story>
<story>
<id>27</id>
<storyText>MoveCamera 2 3,5,112,0 4,5,112,0 4000</storyText>
</story>
<story>
<id>28</id>
<storyText>Sleep 500</storyText>
</story>
<story>
<id>29</id>
<storyText>MoveTo 2 6.758,-2.412,18.8 1000</storyText>
</story>
<story>
<id>30</id>
<storyText>Sleep 1000</storyText>
</story>
<story>
<id>31</id>
<storyText>DestroyModel 1</storyText>
</story>
<story>
<id>32</id>
<storyText>DestroyModel 2</storyText>
</story>
<story>
<id>33</id>
<storyText>LockSight</storyText>
</story>
<story>
<id>34</id>
<storyText>End</storyText>
</story>
</root>

\client\Assets\Scripts\GameLogic\StorySystem\StoryManager.cs CG故事管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using Mogo.Game;
using Mogo.Util;
using Mogo.GameData;
using Mogo.Task;
using HMF;

public class StoryManager : IEventManager
{
// 单例模式
private static StoryManager m_instance;
// CG流程由策划定制
private string config_xml = "xml/storyCG";

private int m_currentStoryID = 0;

// 字典里面套字典,然后以ID为外层字典的key是存储数据的好方式
private Dictionary<int, Dictionary<int, String>> m_mapOfStoryMap = new Dictionary<int, Dictionary<int, String>>();

private Queue<String[]> m_commandQueue = new Queue<String[]>();

// 面向接口编程
private Dictionary<int, EntityParent> m_entityList = new Dictionary<int, EntityParent>();

// 对象字典
private Dictionary<int, GameObject> m_objectList = new Dictionary<int, GameObject>();

private Action m_callback = null;
private bool IsShake { get; set; }
private bool IsOpen { get; set; }

public StoryManager()
{
m_entityList.Add(0, MogoWorld.thePlayer);
IsOpen = true;
IsShake = false;
}

public static StoryManager Instance
{
get
{
if (m_instance == null)
{
m_instance = new StoryManager();
}
return m_instance;
}
}

public void AddListeners()
{
EventDispatcher.AddEventListener<SignalEvents>(Events.CommandEvent.CommandEnd, HandleWaitingEvent);
EventDispatcher.AddEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, HandleEnterInstance);
EventDispatcher.AddEventListener(TeachUILogicManager.TEACHUICRASHED, ClearGuide);
}

public void RemoveListeners()
{
EventDispatcher.RemoveEventListener<SignalEvents>(Events.CommandEvent.CommandEnd, HandleWaitingEvent);
EventDispatcher.RemoveEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, HandleEnterInstance);
EventDispatcher.RemoveEventListener(TeachUILogicManager.TEACHUICRASHED, ClearGuide);
}

#region 函数适配
/// <summary>
/// 创建模型,因为是CG,只创建怪兽模型
/// </summary>
/// <param name="id"></param>
/// <param name="model"></param>
/// <param name="mapx"></param>
/// <param name="mapy"></param>
/// <param name="vec"></param>
/// <param name="playBornFX">是否播放出生特效</param>
void CreateModel(int id, int model, int mapx, int mapy, Vector3 vec, bool playBornFX = true)
{
// 创建模型交给专门的怪物逻辑控制类去完成
EntityMonster entity = new EntityMonster();
// 给怪物逻辑控制类的属性赋值
entity.ID = (uint)id;
entity.model = model;
Vector3 Point = new Vector3();

// 在这个位置创建
MogoUtils.GetPointInTerrain((float)mapx / 100, (float)mapy / 100, out Point);

entity.PlayBornFX = playBornFX;
entity.BillBoard = false; // 头顶的标志牌
entity.position = Point;
entity.rotation = vec;
// 创建模型交给怪物逻辑控制类去完成
entity.CreateModel();
// entity类的OnMoveTo方法监听MogoMotor.ON_MOVE_TO事件
// 并获得两个参数,一个是GameObject类型的,一个是Vector3类型的
EventDispatcher.AddEventListener<GameObject, Vector3>(MogoMotor.ON_MOVE_TO, entity.OnMoveTo);
m_entityList[id] = entity;
}


void PlaySfx(int id, int sfxID, int start, Vector3 vec)
{
GameObject sfx = new GameObject();

}
}

NPC

client\Assets\Resources\data\xml\NPCData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<node>
<id_i>150001</id_i>
<name_i>150001</name_i>
<mode_i>150001</mode_i>
<tips_l>160001</tips_l>
<mapx_i>4730</mapx_i>
<mapy_i>4461</mapy_i>
<rotation_l>0.0, 251, 0.0</rotation_l>
<colliderRange_f>5</colliderRange_f>
<dialogBoxImage_i>300</dialogBoxImage_i>
<standbyAction_i>-1</standbyAction_i>
<actionList_m>-1:20,21:30</actionList_m>
<thinkInterval_l>5000,15000</thinkInterval_l>
<idleTimeRange_l>2000,4000</idleTimeRange_l>
</node>
<node>
<id_i>150000</id_i>
<name_i>150000</name_i>
<mode_i>150000</mode_i>
<tips_l>0</tips_l>
<mapx_i>2699</mapx_i>
<mapy_i>3313</mapy_i>
<rotation_l>0.0, 0.0, 0.0</rotation_l>
<colliderRange_f>131</colliderRange_f>
<dialogBoxImage_i>305</dialogBoxImage_i>
<standbyAction_i>-1</standbyAction_i>
<actionList_m>-1:10</actionList_m>
<thinkInterval_l>14000,24000</thinkInterval_l>
<idleTimeRange_l>2000,5000</idleTimeRange_l>
</node>
</root>

\client\Assets\Resources\data\xml\NPCData.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*----------------------------------------------------------------
// Copyright (C) 2013
//
// 模块名:NPCData
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:NPC数据结构
//----------------------------------------------------------------*/

using System.Collections;
using System.Collections.Generic;

namespace Mogo.GameData
{
public class NPCData : GameData<NPCData>
{
public int name { get; protected set; }

public int mode { get; protected set; }
public int mapx { get; protected set; }
public int mapy { get; protected set; }
public List<float> rotation { get; protected set; }
public float colliderRange { get; protected set; }

public int dialogBoxImage { get; protected set; }
public int tips { get; protected set; }

public int standbyAction { get; protected set; }
public Dictionary<int, int> actionList { get; protected set; }
public List<int> thinkInterval { get; protected set; }
public List<int> idleTimeRange { get; protected set; }

static public readonly string fileName = "xml/NPCData";
//static public Dictionary<int, NPCData> dataMap { get; set; }
}
}

\client\Assets\Scripts\Actors\ActorNPC.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using UnityEngine;
using System.Collections;

using Mogo.Game;
using Mogo.Util;

public class ActorNPC : MonoBehaviour
{
#region 变量

public EntityNPC theEntity;
// 头顶的名字
public Transform billboardTrans = null;

#endregion

void Awake()
{
billboardTrans = transform.FindChild("slot_billboard");
}

// 变得可见的时候
void OnBecameVisible()
{
theEntity.SetFlush(true);
EventDispatcher.TriggerEvent<int>(Events.TaskEvent.NPCInSight, (int)theEntity.ID);
}

void OnBecameInvisible()
{
theEntity.SetFlush(false);
}

void OnTriggerEnter(Collider collider)
{
if (collider.tag == "Player" && MogoWorld.thePlayer.CurrentTask != null)
{
// 触发消息
// 靠近NPC
EventDispatcher.TriggerEvent(Events.TaskEvent.CloseToNPC, (int)theEntity.ID);
// NPC转向玩家
// 第一个参数是npc
// 第二个参数是玩家位置
EventDispatcher.TriggerEvent(Events.NPCEvent.TurnToPlayer, MogoWorld.thePlayer.CurrentTask.npc, MogoWorld.thePlayer.Transform);
}
}

void OnTriggerExit(Collider collider)
{
if (collider.tag == "Player")
{
// 触发消息
// 离开NPC,参数是NPC的实体ID
EventDispatcher.TriggerEvent(Events.TaskEvent.LeaveFromNPC, (int)theEntity.ID);
}
}

void Update()
{
if (MogoWorld.thePlayer != null && MogoWorld.thePlayer.sceneId == MogoWorld.globalSetting.homeScene && billboardTrans != null && theEntity != null)
{
BillboardViewManager.Instance.UpdateBillboardPos(billboardTrans.position, theEntity.ID);
}
}
}

\client\Assets\Scripts\GUI\Mogo\MogoObjOptWorker.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using UnityEngine;
using System.Collections;
using System;

public class MogoObjOptWorker : MonoBehaviour
{
Transform m_myTransform;

public Action BecameVisibleCB;
public Action BecameInVisibleCB;

void OnBecameVisible()
{
if (BecameVisibleCB != null)
{
BecameVisibleCB();
}
}

void OnBecameInvisible()
{
if (BecameInVisibleCB != null)
{
BecameInVisibleCB();
}
}
}

\client\Assets\Scripts\GUI\Mogo\MogoObjOpt.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum MogoObjType
{
NPC,
Player,
Dummy
}

public class MogoObjOpt : MonoBehaviour
{
SkinnedMeshRenderer m_smr;
MeshRenderer m_mr;
Transform m_myTransform;
List<Animation> m_listAnimation = new List<Animation>();
List<Animator> m_listAnimator = new List<Animator>();

public MogoObjType ObjType = MogoObjType.NPC;

void Awake()
{
m_myTransform = transform;

// 参数true表示不活跃的也可以找到
if (m_myTransform.GetComponentsInChildren<SkinnedMeshRenderer>(true).Length > 0)
{
m_smr = m_myTransform.GetComponentsInChildren<SkinnedMeshRenderer>(true)[0];

MogoObjOptWorker worker = m_smr.gameObject.AddComponent<MogoObjOptWorker>();

worker.BecameVisibleCB = whenObjBecameInvisible;
worker.BecameVisibleCB = WhenObjBecameVisible;

if (m_myTransform.GetComponentsInChildren<Animation>(true).Length > 0)
{
for (int i = 0; i < m_myTransform.GetComponentsInChildren<Animation>(true).Length; ++i)
{
m_listAnimation.Add(m_myTransform.GetComponentsInChildren<Animation>(true)[i]);
}
}

if (m_myTransform.GetComponent<Animation>() != null)
{
m_listAnimation.Add(m_myTransform.GetComponent<Animation>());
}

if (m_myTransform.GetComponentsInChildren<Animator>(true).Length > 0)
{
for (int i = 0; i < m_myTransform.GetComponentsInChildren<Animator>(true).Length; ++i){

m_listAnimator.Add(m_myTransform.GetComponentsInChildren<Animator>(true)[i]);
}
}

if (m_myTransform.GetComponent<Animator>() != null)
{
m_listAnimator.Add(m_myTransform.GetComponent<Animator>());
}
}
}

void WhenObjBecameVisible()
{
for (int i = 0; i < m_listAnimation.Count; ++i)
{
m_listAnimation[i].enabled = true;
}

for (int i = 0; i < m_listAnimator.Count; ++i)
{
m_listAnimator[i].enabled = true;
}

switch (ObjType)
{
case MogoObjType.NPC:

break;

case MogoObjType.Player:
BillboardViewManager.Instance.ShowBillboard(GetComponent<ActorPlayer>().theEntity.ID, true);
GetComponent<ActorPlayer>().enabled = true;
break;

case MogoObjType.Dummy:
BillboardViewManager.Instance.ShowBillboard(GetComponent<ActorDummy>().theEntity.ID, true);
GetComponent<ActorDummy>().enabled = true;
break;
}
}

void whenObjBecameInvisible()
{
for (int i = 0; i < m_listAnimation.Count; ++i)
{
m_listAnimation[i].enabled = false;
}

for (int i = 0; i < m_listAnimator.Count; ++i)
{
m_listAnimator[i].enabled = false;
}

switch (ObjType)
{
case MogoObjType.NPC:

break;

case MogoObjType.Player:
BillboardViewManager.Instance.ShowBillboard(GetComponent<ActorPlayer>().theEntity.ID, false);
GetComponent<ActorPlayer>().enabled = false;
break;

case MogoObjType.Dummy:
BillboardViewManager.Instance.ShowBillboard(GetComponent<ActorDummy>().theEntity.ID, false);
GetComponent<ActorDummy>().enabled = false;
break;
}
}
}

\client\Assets\Scripts\GameLogic\Entities\EntityNPC.cs NPC的逻辑控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
using UnityEngine;
using System.Collections;

using Mogo.RPC;
using Mogo.Util;
using Mogo.GameData;
using System;
using System.Collections.Generic;

namespace Mogo.Game
{
public class EntityNPC : EntityParent
{
#region 常量

// 粒子
public static readonly string NPC_QUESTION_MARK_NAME = "fx_ui_jt_l.prefab";
public static readonly string NPC_EXCLAMATION_MARK_NAME = "fx_ui_jt_h.prefab";

public static readonly string SIGN_SLOT = "Bip01_Master/Bip01";

// 动画触发条件,参数Action为-1就闲逛,-2就讲话
public static readonly int IDLE_ACTION = -1;

public static readonly int TALKING_ACTION = -2;

#endregion

#region 公共变量

public enum NPCSignState
{
Doing,
Done,
None
}

public NPCSignState signState = NPCSignState.None;

public GameObject NPCDoingSign = null;
public GameObject NPCDoneSign = null;

public int standbyAction;
public Dictionary<int, int> actionList;
public List<int> thinkInterval;
public List<int> idleTimeRange;
public uint idleTime;

#endregion

#region 私有变量

private bool isFrush = false;
private MogoMotorNPC npcMotor;

protected int fixSum;
protected Dictionary<int, int> fixActionList;
protected uint thinkTimer;
protected uint resetTimer;

// Actor开头的都是挂载到游戏对象上的
protected ActorNPC ap;

#endregion

public override void CreateModel()
{
LoggerHelper.Debug("EntityNPC create:" + ID);

int modelID = NPCData.dataMap[(int)ID].mode;

// 没有模型且不是精灵,退出
if (!AvatarModelData.dataMap.ContainsKey(modelID) && modelID != 150000)
return;

// 不是精灵,精灵没有模型
if (modelID != 150000)
{
AvatarModelData data = AvatarModelData.dataMap[modelID];

AssetCacheMgr.GetInstanceAutoRelease(
data.prefabName,
(prefab, guid, gameObject) =>
{
GameObject = gameObject as GameObject;
Transform = (gameObject as GameObject).transform;
Transform.position = position;

Transform.eulerAngles = rotation;

animator = GameObject.GetComponent<Animator>();
SetIdleAction();

motor = null;
npcMotor = GameObject.AddComponent<MogoMotorNPC>();

GameObject.tag = "NPC";
GameObject.layer = 17;

UpdatePosition();

ap = Transform.GetComponent<ActorNPC>();

if (ap == null)
{
Transform.gameObject.AddComponent<ActorNPC>();
ap = Transform.GetComponent<ActorNPC>();
(ap as ActorNPC).theEntity = this;
}

SphereCollider collider = Transform.gameObject.AddComponent<SphereCollider>();
collider.isTrigger = true;
collider.center = Vector3.zero;

collider.radius = NPCData.dataMap[(int)ID].colliderRange / ((Transform.localScale.x < Transform.localScale.y ? Transform.localScale.x : Transform.localScale.y) < Transform.localScale.z ? (Transform.localScale.x < Transform.localScale.y ? Transform.localScale.x : Transform.localScale.y) : Transform.localScale.z);

BillboardLogicManager.Instance.AddInfoBillboard(ID, Transform, this, false);
BillboardLogicManager.Instance.SetHead(this);
EventDispatcher.TriggerEvent<Vector3,uint>(BillboardLogicManager.BillboardLogicEvent.UPDATEBILLBOARDPOS, Transform.position,ID);

MogoFXManager.Instance.AddShadow(GameObject, ID, 1 / Transform.localScale.x, 1 / Transform.localScale.y, 1 / Transform.localScale.z);

if (MogoWorld.thePlayer.CurrentTask != null)
{
if (MogoWorld.thePlayer.CurrentTask.conditionType == 1 && MogoWorld.thePlayer.CurrentTask.condition != null
&& MogoWorld.thePlayer.CurrentTask.condition.Count >= 3 && TaskData.dataMap.Get(MogoWorld.thePlayer.CurrentTask.condition[2]).npc == (int)ID)
{
SetNPCSign(NPCSignState.Doing);
}

if (MogoWorld.thePlayer.CurrentTask.isShowNPCTip == 1)
{
SetNPCSign(NPCSignState.Done);
}
}

((GameObject)gameObject).AddComponent<MogoObjOpt>().ObjType = MogoObjType.NPC;

if (((GameObject)gameObject).GetComponent<Animator>() != null)
{
((GameObject)gameObject).GetComponent<Animator>().enabled = false;
}

if (((GameObject)gameObject).GetComponent<Animation>() != null)
{
((GameObject)gameObject).GetComponent<Animation>().enabled = false;
}

if (!NPCManager.npcEntitiePosition.ContainsKey((uint)ID))
NPCManager.npcEntitiePosition.Add((uint)ID, Transform.position);

EventDispatcher.TriggerEvent(Events.TaskEvent.CheckNpcInRange);

NPCCheckThink();
}
);
}
else // 精灵没有模型
{
GameObject = new GameObject();
GameObject.name = "Sky_NPC";
Transform = GameObject.transform;
Transform.position = position;
Transform.eulerAngles = rotation;

GameObject.tag = "NPC";

UpdatePosition();

ActorNPC ap = Transform.GetComponent<ActorNPC>();
if (ap == null)
{
Transform.gameObject.AddComponent<ActorNPC>();
ap = Transform.GetComponent<ActorNPC>();
(ap as ActorNPC).theEntity = this;
}

SphereCollider collider = Transform.gameObject.AddComponent<SphereCollider>();
collider.isTrigger = true;
collider.center = Vector3.zero;
collider.radius = 5;
}
}

public void SetIdleAction()
{
if (animator != null)
{
animator.SetInteger("Action", standbyAction);
}
}

public void SetTalkAction()
{
if (animator != null)
{
animator.SetInteger("Action", TALKING_ACTION);
}
}

public void TalkEnd(int npcID)
{
if (npcID == ID)
{
SetIdleAction();
}
}

public override void UpdatePosition()
{
Vector3 point;

LoggerHelper.Debug("model info " + position.x + " z: " + position.z);

if (Mogo.Util.MogoUtils.GetPointInTerrain(position.x, position.z, out point) && Transform)
Transform.position = new Vector3(point.x, point.y, point.z);

Transform.eulerAngles = new Vector3(0, rotation.y, 0);
}
}
}

\client\Assets\Scripts\AvatarControl\MogoMotorNPC.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using UnityEngine;
using System.Collections;
using Mogo.Util;
using System;

public class MogoMotorNPC : MonoBehaviour
{
public float rotateSpeed = 360f;

private bool isTurningTo = false;
private Transform turnToTarget;
private Action turnEndAction;

private bool direction = false;

void Awake()
{

}

void Destroy()
{

}

void Update()
{
if (turnToTarget)
{
float deltaTime = Time.deltaTime;
// 计算角度
float deltaAngle = Vector3.Angle(transform.forward, FixPosition(turnToTarget.position) - transform.position);

if (deltaAngle == 0)
{
StopTurnTo();
}
else
{
if (rotateSpeed * deltaTime > deltaAngle)
{
StopTurnTo();
}
else if (direction)
{
transform.Rotate(0, -rotateSpeed * deltaTime, 0);
}
else
{
transform.Rotate(0, rotateSpeed * deltaTime, 0);
}
}
}
}


#region 转向

public void TurnTo(Transform theTarget, Action action = null)
{
float deltaAngle = Vector3.Angle(transform.forward, FixPosition(theTarget.position) - transform.position);

if (deltaAngle > 25 || deltaAngle < -25)
{
isTurningTo = true;
turnToTarget = theTarget;
turnEndAction = action;

Vector2 vsrc = new Vector2(transform.forward.x, transform.forward.z);
Vector2 vdes = new Vector2(theTarget.position.x - transform.forward.x, theTarget.position.z - transform.forward.z);

if (vsrc * vdes.y - vsrc.y * vsrc.x < 0)
direction = false;
else
direction = true;
}
else
{
action();
}
}

private void CheckStopTurnTo(GameObject theEntityGameObject, Vector3 thePosition)
{
if (isTurningTo)
{
if (theEntityGameObject == MogoWorld.thePlayer.GameObject)
{
StopTurnTo();
}
}
}


private void StopTurnTo()
{
isTurningTo = false;
transform.LookAt(FixPosition(turnToTarget.position));
turnToTarget = null;

if (turnEndAction != null)
turnEndAction();
}

// 使用这个脚本挂载的对象的y轴
private Vector3 FixPosition(Vector3 srcPosition)
{
return new Vector3(srcPosition.x, transform.position.y, srcPosition.z);
}

#endregion
}

PathPoint

\client\Assets\Plugins\Utils\PathPoint.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*----------------------------------------------------------------
// Copyright (C) 2013 广州,爱游
//
// 模块名:PathPoint
// 创建者:
// 修改者列表:
// 创建日期:
// 模块描述:寻路点,随便绑在某个物体上,id必须唯一
//----------------------------------------------------------------*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using Mogo.Util;

public class PathPoint : MonoBehaviour
{
// 路径点
public static Dictionary<int, Vector3> pathPointDic = new Dictionary<int, Vector3>();
public static Dictionary<int, float> pathPointDistance = new Dictionary<int, float>();

//
public static Dictionary<int, Vector3> tempPathPointDic = new Dictionary<int, Vector3>();
public static Dictionary<int, float> tempPathPointDistance = new Dictionary<int, float>();

protected static readonly int RANDOM_SUBPATHPOINT = 5;

public int id;
public float distance;

void Start()
{
if (!pathPointDic.ContainsKey(id))
{
pathPoint.Add(id, transform.position);
if (!pathPointDistance.ContainsKey(id))
pathPointDistance.Add(id, distance);
}
}

/// <summary>
/// 添加子路径点
/// </summary>
/// <param name="id"></param>
/// <param name="position">位置向量</param>
/// <param name="stopDistance">停止距离</param>
protected static void AddSubPathPoint(int id, Vector3 position, float stopDistance)
{
if (!pathPointDic.ContainsKey(id))
{
pathPointDic.Add(id, position);
if (!pathPointDistance.ContainsKey(id))
pathPointDistance.Add(id, stopDistance);
}
}

/// <summary>
/// 得到最近的地点
/// </summary>
/// <param name="playerPosition">玩家位置</param>
/// <param name="main"></param>
/// <param name="stardardPosition"></param>
/// <returns>字典索引</returns>
public static int GetFixNearestPlace(Vector3 playerPosition, int main, Vector3 stardardPosition)
{
int result = 0;
tempPathPointDic.Clear();
tempPathPointDistance.Clear();

if (pathPointDistance[main] <= 1.5)
return result;

// 导航系统计算出的路径。
NavMeshPath path = new NavMeshPath();
// NavMesh.CalculatePath(Vector3 sourcePosition所请求路径的初始位置, Vector3 targetPosition所请求路径的最终位置,int areaMask一个位域掩码,指定在计算路径时可以通过哪些NavMesh区域, AI.NavMeshPath path结果路径);计算两点之间的路径并存储结果路径。
if (!NavMesh.CalculatePath(playerPosition, pathPointDic[main], -1, path))
return result;
// NavMeshPath.corners路径的路点(waypoints)
if (path.corners.Length < 2)
return result;

for (int i = 0; i < RANDOM_SUBPATHPOINT; i++)
{
Vector3 fixPosition = GetRandomVector3InRangeCircle(pathPointDistance[main] - 1.5f);

tempPathPointDic.Add(100 * main + i, fixPosition + stardardPosition);
tempPathPointDistance.Add(100 * main + i, 1);
}

Vector3 turn = path.corners[path.corners.Length - 2];

float standardDistance = Vector3.Distance(turn, pathPointDic[main]);
float resultDistance = standardDistance;
float offset = standardDistance;

foreach (var tempData in tempPathPointDic)
{
float tempSourceDistance = Vector3.Distance(pathPointDic[main], tempData.Value);
if (tempSourceDistance < 0.6f)
{
continue;
}

float tempDistance1 = Vector3.Distance(tempData.Value, pathPointDic[main]);
float tempDistance2 = Vector3.Distance(tempData.Value, turn);

if (tempDistance1 < standardDistance && tempDistance2 < standardDistance
&& tempDistance1 > 0 && tempDistance2 > 0)
{
return tempData.Key;
}
}

return result;
}

public static int GetNearestPlace(Vector3 playerPosition, int main)
{
return GetFixNearestPlace(playerPosition, main, pathPointDic[main]);
}

private static System.Random globalRandomGenerator = Utils.CreateRandom();

public static float GetRandomFloat(float min, float max)
{
if (min < max)
return (float)globalRandomGenerator.NextDouble() * (max - min) + min;
else
return max;
}

public static Vector3 GetRandomVector3InRangeCircle(float rangeTo, float rangeFrom = 0, float angleTo = 360, float angleFrom = 0, floay y = 0)
{
float length = GetRandomFloat(rangeFrom, rangeTo);
float angle = GetRandomFloat(angleFrom, angleTo);

return new Vector3((float)(length * Math.Sin(angle * Math.PI / 180.0)),
y,
(float)(length * Math.Cos(angle * Math.PI / 180.0)));
}
}

\client\Assets\Plugins\Utils\SubPathPoint.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using UnityEngine;
using System.Collections.Generic;
using System;
using Mogo.Util;

public class SubPathPoint : PathPoint
{
public int mainPathPoint;

public static Dictionary<int, List<int>> pathPointLink = new Dictionary<int, List<int>>();

void Start()
{
PathPoint.AddSubPathPoint(id, transform.position, distance);
AddMainPathPointLink();
}

public void AddMainPathPointLink()
{
if (!pathPointLink.ContainsKey(mainPathPoint))
pathPointLink.Add(mainPathPoint, new List<int>());

if (pathPointLink[mainPathPoint] == null)
pathPointLink[mainPathPoint] = new List<int>();

if (!pathPointLink[mainPathPoint].Contains(id))
pathPointLink[mainPathPoint].Add(id);
}

public static int GetNearestSubPoint(Vector3 playerPosition, int main)
{
if (!pathPointLink.ContainsKey(main))
return main;

if (pathPointLink[main] == null)
return main;

int result = 0;
float resultDistance = 0;

if (pathPointDic.ContainsKey(main))
{
result = main;
resultDistance = Vector3.Distance(playerPosition, pathPointDic[main]);
}

foreach (int subID in pathPointLink[main])
{
float tempDistance = -1;

if (pathPointDic.ContainsKey(subID))
tempDistance = Vector3.Distance(playerPosition, pathPointDic[subID]);

if (tempDistance < resultDistance && tempDistance >= 0)
{
result = subID;
resultDistance = tempDistance;
}
}

return result;
}
}

新手引导

\client\Assets\Resources\data\xml\guide.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<node>
<id_i>91</id_i>
<group_i>17</group_i> <!-- 组 -->
<step_i>1</step_i> <!-- 第几步 -->
<conditionEvent_i>2</conditionEvent_i>
<event_arg1_s>90+</event_arg1_s>
<guideLevel_l>90,99</guideLevel_l>
<guideTimes_i>1</guideTimes_i>
<target_i>6</target_i>
<target_arg2_s>0</target_arg2_s>
<!-- 15000是精灵NPCData.xml -->
<!--
9110是ChineseData.xml的数据
您在战斗中获得了竞技场积分,我们看一下它能怎样使用吧!
-->
<openDialog_s>150000 305 9110</openDialog_s>
</node>
<node>
<id_i>92</id_i>
<group_i>17</group_i>
<step_i>2</step_i>
<target_i>2</target_i>
<target_arg1_s>7</target_arg1_s>
<target_arg2_s>1</target_arg2_s>
<text_i>9111</text_i>
</node>
<node>
<id_i>93</id_i>
<group_i>17</group_i>
<step_i>3</step_i>
<target_i>2</target_i>
<target_arg1_s>204</target_arg1_s>
<target_arg2_s>1</target_arg2_s>
<text_i>9112</text_i>
</node>
<node>
<id_i>94</id_i>
<group_i>17</group_i>
<step_i>4</step_i>
<target_i>6</target_i>
<target_arg2_s>0</target_arg2_s>
<openDialog_s>150000 305 9113</openDialog_s>
</node>
</root>

\client\MogoSolution\GameData\GuideXMLData.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Mogo.GameData
{
public class GuideXMLData : GameData<GuideXMLData>
{
/// <summary>
/// 表格对应数据
/// </summary>
public int group { get; protected set; } // 组
public int step { get; protected set; } // 第几步
public Dictionary<string,string> restriction { get; protected set; } // 限制
public int conditionEvent { get; set; } // 触发条件
public string event_arg1 { get; set; }
public string event_arg2 { get; set; }
public List<int> guideLevel { get; set; } // 引导等级
public int guideTimes { get; set; } // 引导事件
public int priority { get; set; } // 优先级
public int target { get; set; } // 目标
public string target_arg1 { get; set; }
public string target_arg2 { get; set; }
public int openGUI { get; set; }
public string openDialog { get; set; } // 打开对话
public int text { get; set; }
static public readonly string fileName = "xml/guide";

/// <summary>
/// 二次抽取数据
/// </summary>
private static List<int> m_events;

static public List<int> ConditionEvent
{
get
{
if (m_events == null)
{
#if UNITY_IPHONE
m_events = new List<int>();
foreach(var v in dataMap)
{
m_events.Add(v.value.conditionEvent);
}
// Distinct是List自带的函数,去重
m_events.Distinct();
#else
m_events = (from data in dataMap select data.value.conditionEvent).Distinct().ToList();
#endif
}
return m_events;
}
}

private static Dictionary<int, List<int>> m_links;

static public Dictionary<int, List<int>> Links
{
get
{
if (m_links == null)
{
#if UNITY_IPHONE
m_links = new Dictionary<int, List<int>>();
List<int> temp;
foreach(var v in dataMap)
{
if (!m_links.ContainsKey(v.Value.group))
{
temp = new List<int>();
temp.Add(v.Value.step);
m_links.Add(v.Key, temp);
}
else
{
m_links[v.Value.group].Add(v.Value.step);
/*
List.sort()有四种形式:
如果T是基本类型的话,.sort()是可以直接使用的,比如List、List等
如果是我们自己定义的Class的话,我们就必须自己定义,这时我们需要:IComparer接口中的Compare函数
1. .sort()
在List中的元素对象必须继承于IComparer,而且实现了CompareTo()方法,基本上所有的值类型都有,也包括string等
2. .sort(IComparer<T>)
基于自定义的类实现
3. .sort(IComparison<T>)
4. .sort(int32,int32,IComparer<T>)
是第二种形式的扩展,可以规定排序范围
*/
m_links[v.Value.group].Sort();
}
}
#else
m_links = dataMap.GroupBy(a => a.Value.group)
.ToDictionary(gdc => gdc.Key,
gdc => gdc.OrderBy(x=>x.Value.step)
.Select(x=>x.Value.id)
.ToList());
#endif
}
return m_links;
}
}

/// <summary>
/// 数据读取方法,包括切片和联合
/// </summary>
/// <param name="events"></param>
/// <returns></returns>
static public List<int> GetIDByEvent(int events)
{
#if UNITY_IPHONE
List<int> lRet=new List<int>();
foreach(var v in dataMap)
{
if(v.Value.conditionEvent==events)
{
lRet.Add(v.Key);
}
}
return lRet;
#else
var d = from t in dataMap
where t.Value.conditionEvent == events
select t.Key;
return d.ToList();
#endif
}
}
}

\client\Assets\Scripts\GameLogic\GuideSystem\GuideSystem.cs 新手指引系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Mogo.Game;
using Mogo.Util;
using Mogo.GameData;

public class GuideSystem : IEventManager
{
// 单例模式
private static GuideSystem m_instance;
private Dictionary<int, int> _guideTimes;

// 引导队列
private List<int> m_guideQueue = new List<int>();
public bool IsOpen{ get; set; }
// 是否有引导对话
private bool m_isGuideDialog = false;

public bool IsGuideDialog
{
get
{
return m_isGuideDialog;
}
set
{
m_isGuideDialog = value;
}
}

public Dictionary<int, int> guideTimes
{
get
{
// SystemConfig.Instance.GuideTimes格式
// Dictionary<ulong, String>()
if (SystemConfig.Instance.GuideTimes.ContainsKey(MogoWorld.thePlayer.dbid))
{
// 将字典字符串转换为键类型与值类型都为整型的字典对象。
_guideTimes = Utils.ParseMapIntInt(SystemConfig.Instance.GuideTimes[MogoWorld.thePlayer.dbid], '!', '?');
}
else
{
_guideTimes = new Dictionary<int, int>();
}
return _guideTimes;
}
}

public GuideSystem()
{
IsOpen = true;
}

public static GuideSystem Instance
{
get
{
if (m_instance == null)
{
m_instance = new GuideSystem();
}
return m_instance;
}
}

#region implement interface

public void AddListeners()
{
EventDispatcher.AddEventListener<int, bool>(Events.InstanceEvent.InstanceUnLoaded, HandleLeaveInstance);
EventDispatcher.AddEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, HandleEnterInstance);
}

public void RemoveListeners()
{
EventDispatcher.RemoveEventListener<int, bool>(Events.InstanceEvent.InstanceUnLoaded, HandleLeaveInstance);
EventDispatcher.RemoveEventListener<int, bool>(Events.InstanceEvent.InstanceLoaded, HandleEnterInstance);
}
#endregion

void HandleEnterInstance(int sceneID, bool isInstance)
{
GuideSystem.Instance.TriggerEvent<int>(GlobalEvents.EnterInstance, sceneID);
}

void HandleLeaveInstance(int sceneID, bool isInstance)
{
GuideSystem.Instance.TriggerEvent<int>(GlobalEvents.LeaveInstance, sceneID);

if (isInstance)
{
var level = MogoWorld.thePlayer.level;
if (level > 0)
{
GuideSystem.Instance.TriggerEvent<byte>(GlobalEvents.ChangeLevel, level);
}
}
}

private void execute(int id)
{
StoryManager.Instance.AddCommand("StartGuideUI");
StoryManager.Instance.AddCommand("Wait 8");

foreach (var index in GuideXMLData.Links[GuideXMLData.dataMap.Get(id).group])
{
ConvertToCommand(index);
}

StoryManager.Instance.AddCommand("UnlockQueue");
StoryManager.Instance.AddCommand("End");
StoryManager.Instance.PrintCommandQueue();
StoryManager.Instance.SetCallBack(
() =>
{
LoggerHelper.Warning("Guide Complete" + id);
GuideSystem.Instance.IsGuideDialog = false;
SaveGuide(id);
dequeueGuide();
}
);
StoryManager.Instance.Execute();
}
}

Utils

\client\Assets\Scripts\Utils\MogoUtils.cs 游戏逻辑相关通用工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System.Collections.Generic;
using System.Linq;
using System;
using UnityEngine;
using Mogo.Game;
using System.Security;
using Mogo.GameData;
using System.Text.RegularExpressions;

namespace Mogo.Util
{
// 静态工具类
public static class MogoUtils
{
/// <summary>
/// 传入x,z,获得在这个方向上Terrain层是否有物体
/// 使用了out参数,一个函数可获得两个结果
/// </summary>
/// <param name="x">x位置</param>
/// <param name="z">z位置</param>
/// <param name="point">投射点</param>
/// <returns>这个位置的地面上是否有物体</returns>
static public bool GetPointInTerrain(float x, float z, out Vector3 point)
{
RaycastHit hit;
// 第四个参数是只投射此层,忽略其他层
var flag = Physics.Linecast(new Vector3(x, 1000, z), new Vector3(x, -1000, z), out hit, (int)LayerMask.NameToLayer("Terrain"));

// 有碰撞到东西
if(flag)
{
point = new Vector3(hit.point.x, hit.point.y + 0,2f, hit.point.z);
return true;
} else
{
point = new Vector3(x, 50, z);
LoggerHelper.Warning("hit noting:" + x + "," + z);
return false;
}
}
}

/// <summary>
/// 返回角色周围指定半径范围内的所有对象。
/// </summary>
/// <param name="t"></param>
/// <param name="radius">半径</param>
/// <param name="layerMask">只获得这些层,其他的忽略</param>
/// <returns></returns>
static public List<List<uint>> GetEntitiesInRange(this Transform t, float radius, float offsetX = 0, float offfsetY = 0, float angleOffset = 0, LayerMask layerMask = LayerMask.Monster | LayerMask.Character | LayerMask.Trap)
{
return GetEntitiesInRange(t.localToWorldMatrix, t.rotation, t.forward, t.position, radius, offsetX, offsetY, angleOffset, layerMask);
}

static public List<List<uint>> GetEntitiesInRange(Vector3 position, float radius, float offsetX = 0, float offsetY = 0, float angleOffset = 0, LayerMask layerMask = LayerMask.Monster | LayerMask.Character | LayerMask.Trap)
{
List<List<uint>> list = new List<List<uint>>();
List<uint> listDummy = new List<uint>();
List<uint> listMonster = new List<uint>();
List<uint> listPlayer = new List<uint>();
List<uint> listMercenary = new List<uint>();

if (!MogoWorld.Entities.ContainsKey(MogoWorld.thePlayer.ID))
{
MogoWorld.Entities.Add(MogoWorld.thePlayer.ID, MogoWorld.thePlayer);
}
// 遍历entities
foreach (KeyValuePair<uint, Mogo.Game.EntityParent> pair in MogoWorld.Entities)
{
EntityParent entity = pair.Value;
if (!entity.Transform)
{
continue;
}
if ((1 << entity.Transform.gameObject.layer & (int)layerMask) == 0) continue;

float entityRadius = ((float)entity.GetIntAttr("scaleRadius")) / 100f;
if ((position - entity.Transform.position).magnitude > radius + entityRadius) continue;

if (pair.Value is EntityDummy)
{
listDummy.Add(pair.Key);
}
else if (pair.Value is EntityMonster)
{
listMonster.Add(pair.Key);
}
else if (pair.Value is EntityPlayer)
{
listPlayer.Add(pair.Key);
}
else if (pair.Value is EntityMercenary)
{
listMercenary.Add(pair.Key);
}

}
MogoWorld.Entities.Remove(MogoWorld.thePlayer.ID);
list.Add(listDummy);
list.Add(listMonster);
list.Add(listPlayer);
list.Add(listMercenary);

return list;
}

static public List<Transform> GetTransformsInSector(this Transform t, Dictionary<Transform, float> transformDic, float radius, float angle = 180f, LayerMask layerMask = LayerMask.Trap)
{
List<Transform> resultList = new List<Transform>();

// 遍历entities
foreach (KeyValuePair<Transform, float> pair in transformDic)
{
if ((1 << pair.Key.gameObject.layer & (int)layerMask) == 0) continue;

float entityRadius = pair.Value;
if ((t.position - pair.Key.position).magnitude > radius + entityRadius) continue;

// 得到切线与(目标物体到人物线)的夹角a
float a = Mathf.Asin(entityRadius / (pair.Key.position - t.position).magnitude);

// 得到目标点与人物正前方的夹角b
float b = Vector3.Angle((pair.Key.position - t.position), t.forward);

// 判断b-a是否在angle/2内
if ((b - a) > angle / 2) continue;

resultList.Add(pair.Key);
}
return resultList;
}
}

SkillManager

\client\Assets\Scripts\GameLogic\Enum\Constants.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System.Collections.Generic;
using Mogo.GameData;
using Mogo.AI;
using Mogo.AI.BT;
using System;
using System.Reflection;

namespace Mogo.Game
{
public enum TargetType
{
Enemy = 0, // 敌人
Myself = 1, // 自己
TeamMember = 2, // 队友
Ally = 3 // 友方
}

public enum TargetRangeType
{
LineRange = 3,
SectorRange = 0,
CircleRange = 1,
SingeTarget = 2,
WorldRange = 6
}
}

\client\Assets\Resources\data\xml\SkillAction.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkillAction>
<id_i>1001</id_i>
<action_i>1</action_i>
<duration_i>0</duration_i>
<nextHitTime_i>500</nextHitTime_i>
<actionBeginDuration_i>120</actionBeginDuration_i>
<actionEndDuration_i>380</actionEndDuration_i>
<enableStick_i>0</enableStick_i>
<teleportDistance_f>0</teleportDistance_f>
<damageTriggerFrame_i>10</damageTriggerFrame_i>
<hitAction_l>11</hitAction_l>
<deadAction_l>17</deadAction_l>
<hitSfx_l>1011111,0.3</hitSfx_l>
<hitExtraSpeed_f>3</hitExtraSpeed_f>
<hitExtraSt_f>0.12</hitExtraSt_f>
<hitExtraSl_f>0.1</hitExtraSl_f>
<sfx_m>910118:0.15</sfx_m>
<targetType_i>0</targetType_i>
<targetRangeType_i>0</targetRangeType_i>
<targetRangeParam_l>400, 150</targetRangeParam_l>
<maxTargetCount_i>20</maxTargetCount_i>
<damageFlag_i>1</damageFlag_i>
<damageMul_f>0.48</damageMul_f>
<damageAdd_i>2</damageAdd_i>
<casterHeal_l>0, 0</casterHeal_l>
<casterAddBuff_l>132</casterAddBuff_l>
<actionDuration_i>500</actionDuration_i>
<castPosType_i>1</castPosType_i>
</SkillAction>
<SkillAction>
<id_i>1002</id_i>
<action_i>1</action_i>
<duration_i>0</duration_i>
<nextHitTime_i>500</nextHitTime_i>
<actionBeginDuration_i>120</actionBeginDuration_i>
<actionEndDuration_i>380</actionEndDuration_i>
<enableStick_i>0</enableStick_i>
<teleportDistance_f>0</teleportDistance_f>
<damageTriggerFrame_i>10</damageTriggerFrame_i>
<hitAction_l>11</hitAction_l>
<deadAction_l>17</deadAction_l>
<hitSfx_l>1011111,0.3</hitSfx_l>
<hitExtraSpeed_f>3</hitExtraSpeed_f>
<hitExtraSt_f>0.12</hitExtraSt_f>
<hitExtraSl_f>0.1</hitExtraSl_f>
<sfx_m>910118:0.15</sfx_m>
<targetType_i>0</targetType_i>
<targetRangeType_i>0</targetRangeType_i>
<targetRangeParam_l>400, 150</targetRangeParam_l>
<maxTargetCount_i>20</maxTargetCount_i>
<damageFlag_i>1</damageFlag_i>
<damageMul_f>0.496</damageMul_f>
<damageAdd_i>4</damageAdd_i>
<casterHeal_l>0, 0</casterHeal_l>
<casterAddBuff_l>132</casterAddBuff_l>
<actionDuration_i>500</actionDuration_i>
<castPosType_i>1</castPosType_i>
</SkillAction>
</root>

\client\MogoSolution\GameData\SkillAction.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using System;
using System.Collections.Generic;

namespace Mogo.GameData
{
public class SkillAction : GameData<SkillAction>
{
// 客户端表现
public int cameraTweenId { get; protected set; }
public float cameraTweenST { get; protected set; }
public float cameraTweenSL { get; protected set; }
public string sound { get; protected set; }
public float soundST { get; protected set; }
public string soundHit { get; protected set; }
public int freeze { get; protected set; }
public int removeCollider { get; protected set; }
public int replication { get; protected set; }
public int action { get; protected set; }
public int duration { get; protected set; }
public int actionTime { get; protected set; }
public int nextHitTime { get; protected set; }
public int enableStick { get; protected set; }
public float extraSpeed { get; protected set; }
public float extraSt { get; protected set; }
public float extraSl { get; protected set; }
public float teleportDistance { get; protected set; }
public int damageTriggerFrame { get; protected set; }
public List<int> hitAction { get; protected set; }
public List<int> deadAction { get; protected set; }
public List<float> hitSfx { get; protected set; }
public float hitExtraSpeed { get; protected set; }
public float hitExtraSt { get; protected set; }
public float hitExtraSl { get; protected set; }
public List<int> hitHover { get; protected set; }
public List<int> hitFly { get; protected set; }
public float stiff { get; protected set; }
public Dictionary<int, float> sfx { get; protected set; }
public List<int> entitys { get; protected set; }
public List<int> spawnPos { get; protected set; }

// 技能效果
public float hitXoffset { get; protected set; }
public float hitYoffset { get; protected set; }
public float angleOffset { get; protected set; }
public int targetType { get; protected set; }
public int targetRangeType { get; protected set; }
public List<float> targetRangeParam { get; protected set; } // xml以“,”分割的用List存储
public int maxTargetCount { get; protected set; }
public int activeBuffMode { get; protected set; }
public int activeBuff { get; protected set; }
public int damageFlag { get; protected set; }
public float damageMul { get; protected set; }
public int damageAdd { get; protected set; }
public List<int> heal { get; protected set; }
public List<int> casterAddBuff { get; protected set; }
public List<int> casterDelBuff { get; protected set; }
public List<int> targetAddBuff { get; protected set; }
public List<int> targetDelBuff { get; protected set; }
public List<int> casterHeal { get; protected set; }
public List<int> targetHeal { get; protected set; }
public int actionDuration { get; protected set; }
public int castPosType { get; protected set; }
public int actionBeginDuration { get; protected set; }
public int actionEndDuration { get; protected set; }

public int triggerEvent { get; protected set; }
public int spawnPoint { get; protected set; }

static public readonly string fileName = "xml/SkillAction";
//static public Dictionary<int, SkillAction> dataMap { get; set; }
}
}

\client\Assets\Scripts\GameLogic\SkillSystem\SkillManager.cs 技能管理系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using Mogo.Game;
using Mogo.GameData;
using Mogo.Util;
using Mogo.FSM;

public class SkillManager
{
public static bool showSkillRange = false;
protected EntityParent theOwner;

public SkillManager(EntityParent owner)
{
theOwner = owner;
}

virtual public void Clean()
{
}

#region 攻击

/// <summary>
///
/// </summary>
/// <param name="hitActionID"></param>
/// <param name="ltwm"></param>
/// <param name="rotation"></param>
/// <param name="forward"></param>
/// <param name="position"></param>
public void AttackEffect(int hitActionID, Matrix4x4 ltwm, Quaternion rotation, Vector3 forward, Vector3 position)
{
// 技能数据实体对象
SkillAction s = SkillAction.dataMap[hitActionID];
if (s.damageFlag == 0) // 等于1才有伤害
{
return;
}
// dummyList,MonsterList,PlayerList,MercenaryList
List<List<uint>> list = GetHitEntities(hitActionID, ltwm, rotation, forward, position);

List<Transform> hitContainers = GetHitContainers(hitActionID);

if (list.Count != 4)
{
return;
}


}

// 根据技能id, 获取到受攻击者列表。
// 返回值是一个三元组。 分别是dummy list,monster list, player list
/// <param name="hitActionID">技能id</param>
/// <param name="ltwm"></param>
/// <param name="rotation"></param>
/// <param name="forward"></param>
/// <param name="position"></param>
private List<List<uint>> GetHitEntities(int hitActionID, Matrix4x4 ltwm, Quaternion rotation, Vector3 forward, Vector3 position)
{
var skillData = SkillAction.dataMap[hitActionID];

// 目标类型 0敌人,1自己,2队友,3友方
int targetType = skillData.targetType;
// 攻击范围类型。0扇形范围,1圆形范围,2单体,3直线范围,4前方范围
int targetRangeType = skillData.targetRangeType;
// 攻击范围参数。 针对不同类型,有不同意义。浮点数列表
List<float> targetRangeParam = skillData.targetRangeParam;

float offsetX = skillData.hitXoffset;
float offsetY = skillData.hitYoffset;
float angleOffset = 180;

List<List<uint>> entities = new List<List<uint>>();

if (targetType == (int)TargetType.Myself)
{
List<uint> listDummy = new List<uint>();
List<uint> listMonster = new List<uint>();
List<uint> listPlayer = new List<uint>();
List<uint> listMercenary = new List<uint>();

listPlayer.Add(theOwner.ID);

entities.Add(listDummy);
entities.Add(listMonster);
entities.Add(listPlayer);
entities.Add(listMercenary);
return entities;
}

if (theOwner.Transform == null)
{
return entities;
}
// 获得技能拥有者的信息
// 将点从局部空间转换为世界空间的矩阵(只读)。
Matrix4x4 entityltwm = theOwner.Transform.localToWorldMatrix;
Quaternion entityrotation = theOwner.Transform.rotation;
Vector3 entityforward = theOwner.Transform.forward;
Vector3 entityposition = theOwner.Transform.position;

if (skillData.castPosType == 0)
{
entityltwm = ltwm;
entityrotation = rotation;
entityforward = forward;
entityposition = position;
}

TargetRangeType rangeType = (TargetRangeType)targetRangeType;

// 根据攻击范围,确定受击者的逻辑控制类
switch (rangeType)
{
case TargetRangeType.CircleRange: // 400,50 猜测半径400
if (targetRangeParam.Count >= 1)
{
float radius = targetRangeParam[0] * 0.01f; // 半径 = 400 * 0.01
// 怪物在攻击
if (skillData.castPosType == 2 && theOwner is EntityDummy)
{
EntityParent e = theOwner.GetTargetEntity();

if (e != null)
{
// 角色周围指定半径范围内的所有对象。
entities = MogoUtils.GetEntitiesInRange(e.Transform.position, radius, offsetX, offsetY, angleOffset);
}
}
else
{
entities = MogoUtils.GetEntitiesInRange(entityltwm, entityrotation, entityforward, entityposition, radius, offsetX, offsetY, angleOffset);
}
}
break;


}
}

#endregion
}

LuaTable

\client\MogoSolution\LoaderLib\Utils\LuaTable.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using System;
using System.Collections.Generic;

/// <summary>
/// 固定键值对为string与object的Lua table字典
/// </summary>
public class LuaTable : IEnumerable<KeyValuePair<string, object>>
{
private Dictionary<string, object> m_dic;
private Dictionary<string, bool> m_keyIsStringDic;


}


/*
public static class StringExtension
{
public static void Foo(this string s)
{
Console.WriteLine("Foo invoked for {0}", s);
}
}
为什么这里会有一个this关键字,做什么用?其实这就是扩展方法!这个扩展方法在静态类中声明,定义一个静态方法,其中第一个参数定义扩展类型。Foo()方法扩展了String类,因为它的第一个参数定义了String类型,为了区分扩展方法和一般的静态方法,扩展方法还需要给第一个参数使用this关键字。

现在就可以使用带string类型的Foo方法了:

string s="Hello";
s.Foo();

结果在控制台上显示Foo invoked for Hello,因为Hello是传送给Foo方法的字符串。

归纳:扩展方法可以写入最初没有提供该方法的类中。还可以把方法添加到实现某个接口的任何类中,这样多个类可以使用相同的实现代码。
*/
public static class DictionaryExtend
{
// 泛型扩展
// default(T)可以得到该类型的默认值
// 这两者相同:int myInt = default(int);int myInt = 0;
// default(object) is null
// default(T)在泛型编成中如果不限制T类型参数是值类型或引用类型的话,程序内部可能会出现错误,因为值类型不允许NULL。所以default用来获取一个类型的默认值,对于值类型得到new T()基本得到的都是0;对于引用类型会得到Null。或者你不使用Default关键词,自己通过反射得到T是指类型还是引用类型,然后设置默认值
// 这边T没有设置范围,比如之前会写class,new()什么的,这儿没写,就只有通过default(T)才能判断是否等于null,直接T == null是要报错的。

// 三个重写,分别是数组,List和Dictionary的Get()方法
// 对任意对象增加Get方法的话是this T t
public static T Get<T>(this T[] array, int index)
{
if (array == null)
{
Mogo.Util.LoggerHelper.Critical("Array is null.");
return default(T) == null ? GetDefaultValue<T>() : default(T);
}
else if (array.Length <= index)
{
Mogo.Util.LoggerHelper.Critical(String.Format("Index '{0}' is out of range.", index));
return default(T) == null ? GetDefaultValue<T>() : default(T);
}
else
{
return array[index];
}
}

// List的Get()方法
public static T Get<T>(this List<T> list, int index)
{
if (list == null)
{
Mogo.Util.LoggerHelper.Critical("List is null.");
return default(T) == null ? GetDefaultValue<T>() : default(T);
}
else if (list.Count <= index)
{
Mogo.Util.LoggerHelper.Critical(String.Format(
"Index '{0}' is out of range.", index));
return default(T) == null ? GetDefaultValue<T>() : default(T);
}
else
{
return list[index]
}
}

public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary == null)
{
Mogo.Util.LoggerHelper.Critical("Dictionary is null.");
return default(TValue) == null ? GetDefaultValue<TValue>() : default(TValue);
}
else if (!dictionary.ContainsKey(key))
{
Mogo.Util.LoggerHelper.Critical(String.Format("Key '{0}' is not exist.", key));
return default(TValue) == null ? GetDefaultValue<TValue>() : default(TValue);
}
else
{
return dictionary[key];
}
}

public static T GetDefaultValue<T>()
{
// 有一个无参构造函数或没有显示声明带参的构造函数
var constructor = typeof(T).GetConstructor(Type.EmptyTypes);
if (constructor == null)
return default(T);
else
return (T)constructor.Invoke(null);
}

public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
TValue value;
return dictionary.TryGetValue(key, out value) ? value : defaultValue;
}

public static TValue GetValueOrDefaultValueProvider<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TValue> defaultValueProvider)
{
TValue value;
return dictionary.TryGetValue(key, out value) ? value : defaultValueProvider();
}
}
0%