官方MegaCity案例结束了,其实还有很多细节没有去细讲,因为我比较着急想要继续学习大鹏的UnityMMO案例,这个案例比较接近我想要开发的游戏。我和大鹏沟通过了,大鹏的UnityMMO是打算弄成塞达尔或者奥德赛那样,有多种多样的玩法。
而我,更想开发一款大世界生存动作手游,这款手游将结合《剑灵》的战斗模式设计,这是我比较喜欢动作设计模式。但是我会在操作上做大幅度减法,实际上是一种极简主义,因为手机的操作局限性,我不得不做这种大减法。
在做减法的同时让我想起了儿时常玩的街机三国,这款游戏对我影响很深,我至今仍然记得触发连招的快感。我企图还原那种快感,于是我的解决方案就出来了,街机三国的触发模式+剑灵的战斗机制=我的玩法。
在这基础上,我会利用方向键+按钮的组合键触发出无限连招,这套连招有无数分支,根据触发的前置技能的不同,而后续的连招走向一个独特的分支。例如,刺客的袖箭(→+A)触发,命中(所有无锁定,比较考验操作,也就是说技能完全可能丢空)敌人后会有几率致盲 or 流血 or 中毒 or 眩晕(不同的秘籍会有不同的效果),不同的效果成了下一套技能的前置buff,例如致盲会触发背刺(←+A+B),所以玩家需要关注敌人的状态,从而完成连招。
后续我会写详细的设计文档,把无限连招的玩法完善出来。
在故事和关卡设计上我还有很多想法,例如前所未闻的AI怪物,我们常规的玩法怪物都比较单纯,甚至你会觉得无聊,刷怪只是刷怪。但是我想在这方面做创新,例如怪物除了短期仇恨意外,还有长期仇恨。它们会记住你,并在见到你的时候回忆起来:哎哟,你小子又来找茬来了,上次从老子这里抢走的银鳞胸甲是时候物归原主了。
是的,我想设计与众不同的怪物,他们会有自己的情感(剧情),台词(个性),生存斗争(我会在游戏中设计生存模块,仿饥荒,饥荒中玩家面临生存危机,我将生存危机同样应用到怪物身上,这回是非常有意思的尝试)。
怪物还会主动参与玩家互动,例如抢夺玩家的食材,这是为了生存,饥饿值会促使怪物寻找食物。怪物还会抢夺装备,是的,它们也要武装自己,不仅如此,还会强化,甚至制作装备(根据智力来驱动,不同类型的怪物,特长不同)。
我还有很多想法,例如游戏的生态系统,食物链,轮回系统等等,这些设计,我想很多人想过,但是目前还没有成品的游戏做到。这些想法促使我不断学习和实践,我的内心深处特别想要完成这款特别的游戏,我常常想,这是款超级好玩的游戏。
啰嗦了这么多,想要做成却不是那么容易的事情,总而言之只要我们一步步向着目标前进,总有一天会实现的,一起加油吧!
0下载Unity编辑器(2019.1.4f1 or 更新的版本),if(已经下载了)continue;
1大鹏将项目代码和资源拆分成两部分,所以我们需要分别下载,然后再整合。
命令行下载UnityMMO,打开Git Shell输入:
git clone https://github.com/liuhaopen/UnityMMO.git --recurse
下载完成后,继续输入:
git clone https://github.com/liuhaopen/UnityMMO-Resource.git --recurse
or 点击UnityMMO和UnityMMO-Resource分别下载Zip压缩包
if(已经下载了)continue;
2如果下载的是压缩包,需要先将两个压缩包分别进行解压。然后打开UnityMMO-Resource并把Assets/AssetBundleRes及其meta文件复制到UnityMMO项目的Assets目录里,接下来将UnityMMO添加到Unity Hub项目中;
3用Unity Hub打开大鹏的开源项目:UnityMMO,等待Unity进行编译工作;
4打开项目后,我们发现还需要下载Third Person Controller - Basic Locomotion FREE插件,这个简单,直接在资源商店找到下载导入即可,然后在Assets/XLuaFramework下找到main场景,打开该场景。
梳理下UnityMMO的进度好了,我们完成了游戏资源的整合和服务器部署,大鹏写了启动流程和登录流程,所以我们已经成功进入了游戏的选人场景,如下图所示:
我的Shader出了问题,因为使用的是Unity2019.1.12f,这和大鹏的版本不一致。所以角色变绿了,很尴尬,目前还没有解决,毕竟美术不是我的主攻方向。那么我们这一篇来解析一下选择角色的流程,当登录成功后会直接调用LoginController.lua下面的回调方法,因为对登录成功事件进行了绑定:
--绑定登录成功事件回调函数LoginSucceed
self.login_succeed_handler = GlobalEventSystem:Bind(LoginConst.Event.LoginSucceed, LoginSucceed)
这里要捎带提一下LuaFrameWork,非常成熟的热更新框架,后面会抽时间系统学习一下。这里的GlobalEventSystem:Bind方法可以将系统事件和回调函数绑定起来,在对应事件触发后,调用对应的回调函数。
登录成功后的回调函数如图所示,By the way,这里顺便推荐一下Anders研发的Lua IDE,名字叫做LuaPerfect,非常好用!
回到我们的代码,LoginSucceed的逻辑很清楚:
--向服务器发送获取角色列表请求,回调函数on_ack
NetDispatcher:SendMessage("account_get_role_list", nil, on_ack)
local on_ack = function ( ack_data )
print("Cat:LoginController [start:27] ack_data:", ack_data)
PrintTable(ack_data)
print("Cat:LoginController [end]")
--获取角色列表的数据
local role_list = ack_data.role_list
--设置角色列表数据,后面会在UI上显示
LoginModel:GetInstance():SetRoleList(role_list)
--如果登录窗口开着,则关闭登录窗口
if self.loginView then
UIMgr:Close(self.loginView)
self.loginView = nil
end
--如果角色列表不为空,且长度大于0
if role_list and #role_list > 0 then
--已有角色就先进入选择角色界面
local view = require("Game/Login/LoginSelectRoleView").New()
UIMgr:Show(view)
else
--还没有角色就先进入创建角色界面
local view = require("Game/Login/LoginCreateRoleView").New()
UIMgr:Show(view)
end
end
我之前对lua不是很熟悉,这两天恶补了一下,总算是打通了Lua语言,算是入门级别吧。
创建角色的逻辑全部在LoginCreateRoleView.lua脚本中:
local LoginCreateRoleView = BaseClass()
--实例化的时候调用
function LoginCreateRoleView:DefaultVar( )、
--显示背景
require("Game/Login/LoginSceneBgView"):SetActive(true)
--返回配置
return {
UIConfig = {
prefab_path = "Assets/AssetBundleRes/ui/login/LoginCreateRoleView.prefab",
canvas_name = "Normal",
components = {
{UI.HideOtherView},
{UI.DelayDestroy, {delay_time=5}},
},
},
select_sex = 1,
is_first_select = true,
}
end
--加载后获取对应的UI组件
function LoginCreateRoleView:OnLoad( )
local names = {
"head_scroll/Viewport/head_con","close:obj","create_role:obj","item_con","head_scroll","role_tip:img","random:obj","role_name:input","effect","select_role_con:raw",
}
UI.GetChildren(self, self.transform, names)
self.transform.sizeDelta = Vector2.zero
--添加事件
self:AddEvents()
--初始化视图
self:InitView()
--加载角色模型
self:LoadRolesModel()
end
--随机加载角色模型
function LoginCreateRoleView:LoadRolesModel( )
local sex = math.random(1, 2)
self:SetCurSelectSex(sex)
end
--初始化创建角色的视图
function LoginCreateRoleView:InitView( )
self.sex_item_com = self.sex_item_com or UIMgr:AddUIComponent(self, UI.ItemListCreator)
local info = {
data_list = {1,2},
item_con = self.item_con,
prefab_path = ResPath.GetFullUIPath("login/LoginCreateRoleItem.prefab"),
item_height = 128,
space_y = 5,
scroll_view = self.item_con,
child_names = {"sex:txt","role_head:raw","bg:img:obj",},
on_update_item = function(item, i, v)
item.sex_txt.text = v==1 and "男" or "女"
item.sex_value = v
if not item.UpdateSelect then
item.UpdateSelect = function(item)
local cur_is_select = self.select_sex==v
UIHelper.SetImage(item.bg_img, cur_is_select and "login/login_role_item_bg_sel.png" or "login/login_role_item_bg_nor.png", true)
end
end
item:UpdateSelect()
if not item.UpdateHead then
item.UpdateHead = function(item)
local headRes = ResPath.GetRoleHeadRes(v, 0)
UIHelper.SetRawImage(item.role_head_raw, headRes)
end
end
item:UpdateHead()
if not item.had_bind_click then
item.had_bind_click = true
local on_click = function ( )
print('Cat:LoginCreateRoleView.lua[66] item.sex_value', item.sex_value)
self:SetCurSelectSex(item.sex_value)
end
UIHelper.BindClickEvent(item.bg_obj, on_click)
end
end,
}
self.sex_item_com:UpdateItems(info)
end
--设置当前选择角色的性别
function LoginCreateRoleView:SetCurSelectSex( sex )
print('Cat:LoginCreateRoleView.lua[75] sex', sex)
if self.select_sex == sex and not self.is_first_select then
return
end
self.is_first_select = false
self.select_sex = sex
self.sex_item_com:IterateItems(function(item, i)
if item.UpdateSelect then
item:UpdateSelect()
end
end)
-- self:UpdateRoleHead()
UIHelper.SetImage(self.role_tip_img, "login/login_role_tip_"..sex..".png", true)
self:OnClickRandomName()
self:UpdateRoleMesh(sex)
-- self:PlayRoleSound(sex)
end
--更新角色
function LoginCreateRoleView:UpdateRoleMesh( sex )
local show_data = {
showType = UILooksNode.ShowType.Role,
showRawImg = self.select_role_con_raw,
body = 1,
hair = 1,
career = sex==1 and 1 or 2,
canRotate = true,
}
self.roleUILooksNode = self.roleUILooksNode or UILooksNode.New(self.select_role_con)
self.roleUILooksNode:SetData(show_data)
end
--创建随机角色名
function LoginCreateRoleView:OnClickRandomName( )
local nick_name = nil
math.randomseed(os.clock() * 10000)
self.surname = self.surname or {"开朗哒","开朗的","可爱哒","可爱的","刻苦的",}
self.forename_man = self.forename_man or {"刚史","恭平","光希","光一","圭介",}
self.forename_woman = self.forename_woman or {"菜菜","成美","赤穗","初音","雏子",}
nick_name = self.surname[math.ceil(#self.surname * math.random())]
if self.select_sex == 1 then --临时随机名称,也可以用配置列表
nick_name = nick_name .. self.forename_man[math.ceil(#self.forename_man*math.random())]
else
nick_name = nick_name .. self.forename_woman[math.ceil(#self.forename_woman*math.random())]
end
print('Cat:LoginCreateRoleView.lua[106] nick_name', nick_name)
self.role_name_input.text = nick_name
end
--绑定点击事件,添加事件回调
function LoginCreateRoleView:AddEvents( )
local on_click = function ( click_btn )
print('Cat:LoginCreateRoleView.lua[29] click_btn', click_btn)
if click_btn == self.close_obj then
UIMgr:Close(self)--返回上个界面
elseif click_btn == self.random_obj then
self:OnClickRandomName()--随机姓名
elseif click_btn == self.create_role_obj then
--请求创建角色
local on_ack = function ( ack_data )
print("Cat:LoginCreateRoleView [start:35] ack_data:", ack_data)
PrintTable(ack_data)
print("Cat:LoginCreateRoleView [end]")
if ack_data.result == 0 then
UIMgr:CloseAllView()
require("Game/Login/LoginSceneBgView"):SetActive(false)
--正式进入游戏场景
GlobalEventSystem:Fire(LoginConst.Event.SelectRoleEnterGame, ack_data.role_id)
else
print('Cat:LoginCreateRoleView.lua[37] ack_data.result', ack_data.result)
--创建角色失败,显示消息提示
Message:Show("创建角色失败,错码码:"..ack_data.result)
end
end
--向服务器发送创建角色的消息
NetDispatcher:SendMessage("account_create_role", {name=self.role_name_input.text, career=self.select_sex}, on_ack)
end
end
UIHelper.BindClickEvent(self.close_obj, on_click)
UIHelper.BindClickEvent(self.create_role_obj, on_click)
UIHelper.BindClickEvent(self.random_obj, on_click)
end
function LoginCreateRoleView:OnClose( )
-- require("Game/Login/LoginSceneBgView"):SetActive(false)
end
return LoginCreateRoleView
选人的逻辑相对简单,我就节选部分LoginSelectRoleView.lua脚本好了:
--点击开始按钮后触发
local on_click = function ( click_btn )
if click_btn == self.start_obj then
--发射选择角色进入游戏事件
GlobalEventSystem:Fire(LoginConst.Event.SelectRoleEnterGame, self.select_role_id)
--保存角色编号
CookieWrapper:GetInstance():SaveCookie(CookieLevelType.Common, CookieTimeType.TYPE_ALWAYS, CookieKey.LastSelectRoleID, self.select_role_id)
end
end
--绑定按钮点击事件
UI.BindClickEvent(self.start_obj, on_click)
Ok,选择和创建角色学习完毕,下一篇正式进入游戏!
这一篇的流程大体如下:
如果喜欢我的文章可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证!
如果有技术难题需要讨论,可以加入开发者联盟:566189328(付费群)为您提供有限的技术支持,以及,心灵鸡汤!
当然,不需要技术支持也欢迎加入进来,随时可以请我喝咖啡、茶和果汁!( ̄┰ ̄*)