​1、课设作品需求分析.. 1​

​2、课设作品详细设计.. 1​

​3、课设作品编程实现.. 1​

​3.1场景搭建... 1​

​3.2角色控制... 1​

​3.3游戏UI设计... 1​

​3.4添加声音... 1​

​3.5添加粒子特效.. 1​

​4、课设作品测试.. 1​

​5、课设总结... 1​



1、课设作品需求分析

魔盒世界是一款多人在线第一人称视角射击游戏,它包括客户端和服务器端两个部分,具备账号登录注册功能,使用MySql数据库保存账户数据(后期通过腾讯云MySql进行存储)。

unity实现网络对战游戏_客户端

游戏流程设计

2、课设作品详细设计

客户端单机游戏实现

1、玩家控制设计

unity实现网络对战游戏_客户端_02

2、UI框架设计

UI系统由面板基类PanelBase、面板管理器PanelMgr和多个具体的面板组成,所有的面板都继承自PanelBase,而PanelMgr提供打开某个面板、关闭某个面板的方法。

面板是通过动态加载显示的,这个主要通过PanelMgr去加载显示。面板也设置了层级关系,提示框显示在普通面板的上面,定义了一个PanelLayer类型的枚举来指定面板的层级。整个UI框架实现了代码界面分离。

服务器端设计

unity实现网络对战游戏_客户端_03

3、设计数据库

数据库设计比较简单,新建了一个boxworld数据库,然后添加了user和player表。User表用来保存以后账号和密码,而player表用来保存角色数据。

客户端网络模块设计

unity实现网络对战游戏_服务器端_04

3、课设作品编程实现

3.1场景搭建

场景主要有两大部分,这两部分是左右对称的,首先设计一部分场景然后旋转拼凑就可以了。

如图整体情况。

unity实现网络对战游戏_服务器端_05

整个场景主要是由一个简模做的地形,然后加入水,添加石头、加入花草树木、在水的两边架上一座桥,在水的周围添加木桩。

unity实现网络对战游戏_服务器端_06

unity实现网络对战游戏_客户端_07

unity实现网络对战游戏_ios_08

unity实现网络对战游戏_客户端_09

unity实现网络对战游戏_客户端_10

unity实现网络对战游戏_客户端_11

unity实现网络对战游戏_服务器端_12

unity实现网络对战游戏_ios_13

unity实现网络对战游戏_服务器端_14

3.2角色控制

玩家主要是通过刚体组件来进行移动的,通过Input.GetButton(“Jump”)控制跳跃,Input.GetAxis(“Horizontal”)和Input.GetAxis(“Vertical”)控制玩家移动。

unity实现网络对战游戏_客户端_15

核心代码如下:



void Update ()
{
//技能控制玩家移动速度
if(PlayerSkillEffect.instance.runBtnIsOn)
{
moveSpeed = 6;
}
else
{
moveSpeed = 2.0f;
}
Move ();
Look ();

/*每隔100ms发送一次位置同步信息*/
if (Time.time - lastSendInfoTime > 0.1f)
{
SendUnitInfo ();
lastSendInfoTime = Time.time;
}
//判断鼠标是否在UI上面,如果是就显示鼠标,否在不显示
if (EventSystem.current.IsPointerOverGameObject())
{
//显示鼠标但是不能攻击
Cursor.visible = true;
}
else
{
//不显示鼠标,并且可以攻击
Cursor.visible = false;
LeftAttack();
}
}
void Move ()
{
bool jump = Input.GetButton ("Jump");
Vector3 dir = new Vector3 (Input.GetAxis ("Horizontal"), 0, Input.GetAxis ("Vertical"));
if (IsGrounded ())
{
if (dir.magnitude == 0)
{
if (anim.GetCurrentAnimatorStateInfo (0).IsName ("Move"))
anim.speed = 0;
}
else
{
anim.speed = 1;
if (dir.magnitude > 1)
dir.Normalize ();
Vector3 localDir = transform.TransformDirection (dir);
rb.velocity = localDir * moveSpeed;
anim.SetFloat ("moveSpeed", dir.magnitude * moveSpeed);
}
if (jump)
{
rb.velocity = new Vector3 (rb.velocity.x, jumpPower, rb.velocity.z);
}
}
else
{
if (anim.GetCurrentAnimatorStateInfo (0).IsName ("Move"))
anim.speed = 0;
}
}
bool IsGrounded ()
{
return Physics.CheckBox (transform.position + Vector3.up * 0.1f, new Vector3 (0.2f, 0.2f, 0.2f), Quaternion.identity, 256);
}
void Look ()
{
transform.Rotate (new Vector3 (0, Input.GetAxis ("Mouse X") * rotSpeed, 0));

float rotationX = head.localEulerAngles.x;
rotationX -= Input.GetAxis ("Mouse Y") * rotSpeed;
if (rotationX > 180)
rotationX -= 360;
rotationX = Mathf.Clamp (rotationX, -80f, 40f);
head.localEulerAngles = new Vector3 (rotationX, 0, 0);

}
void LeftAttack ()
{
/*攻击间隔等待完毕就装填箭矢*/
if (holdArrow == null && Time.time - lastLAttTime > intervalLAtt)
{
if(PlayerSkillEffect.instance.redBtnIsOn)
{
holdArrow = Instantiate(redArrowPrefab, muzzle.transform.position, muzzle.transform.rotation);
}
else if(PlayerSkillEffect.instance.blueBtnIsOn)
{
holdArrow = Instantiate(blueArrowPrefab, muzzle.transform.position, muzzle.transform.rotation);
}
else
{
holdArrow = Instantiate(arrowPrefab, muzzle.transform.position, muzzle.transform.rotation);
}
holdArrow.transform.SetParent (muzzle);
//归零蓄力时间
leftHoldtime = 0;
}
if (holdArrow == null)
return;
/*绘制轨迹预判*/
GetAimTrail (Mathf.Clamp (leftHoldtime, 0.5f, 2) * 10, holdArrow.transform.position);
/*鼠标按下蓄力*/
if (Input.GetMouseButton (0))
{
//在按下的第一帧发送蓄力协议
if (leftHoldtime == 0)
SendHoldInfo ();
//累计蓄力时间
leftHoldtime += Time.deltaTime;
/*播放蓄力动作,箭矢往后拉*/
holdArrow.transform.localPosition = Vector3.Lerp (holdArrow.transform.localPosition, new Vector3 (0, 0, -0.1f), Time.deltaTime);
}
/*当松开鼠标则执行发射动作*/
if (Input.GetMouseButtonUp (0))
{
/*不可跳过蓄力状态执行*/
if (leftHoldtime > 0)
{
//添加刚体,因为当父物体和子物体都存在刚体的时候,子物体不会随着父物体移动
Rigidbody harb = holdArrow.AddComponent<Rigidbody> ();
harb.useGravity = false;
harb.constraints = RigidbodyConstraints.FreezeRotation;
//发送协议
SendShootInfo (rb.velocity.x, rb.velocity.y, rb.velocity.z);

//脱离父物体
holdArrow.transform.SetParent (null);
//添加速度
holdArrow.GetComponent<Arrow> ().Launch (this.gameObject, leftHoldtime, rb.velocity);
//重置箭矢装填
lastLAttTime = Time.time;
holdArrow = null;
}
}
}


3.3游戏UI设计

一共设计了七个UI面板,也就是游戏一共有七个不同界面,这些面板是通过动态进行加载的,在合适的时候进行显示和不显示。

unity实现网络对战游戏_客户端_16

unity实现网络对战游戏_服务器端_17

unity实现网络对战游戏_客户端_18

unity实现网络对战游戏_ios_19

unity实现网络对战游戏_客户端_20

unity实现网络对战游戏_客户端_21

unity实现网络对战游戏_客户端_22

unity实现网络对战游戏_客户端_23


3.4添加声音

整个游戏主要包含背景音乐,玩家射击音效,玩家受伤音效,玩家死亡音效,敌人受伤音效,敌人死亡音效。

unity实现网络对战游戏_ios_24

    //音效

    public AudioClip audioClipDead;

    public AudioClip audioClipInjured;

private AudioSource audioSource;

/*播放受伤音效*/

audioSource.spatialBlend = 1;

audioSource.PlayOneShot(audioClipInjured);

3.5添加粒子特效

整个游戏主要包含玩家受伤掉血特效,玩家死亡特效,敌人掉血特效,敌人死亡特效,箭特效,技能特效,玩家包含4个技能,分别是通过按下Q键,可以发送火箭,这个火箭可以对敌人造成致命的伤害,敌人被射中将会出现被燃烧的效果,一箭致命。按下E键玩家可以发射冰箭,敌人会造成严重伤害,产生相应蓝色特效,按下R键,玩家可以回血,这个时候玩家头顶会显示回血值。按下T键,玩家可以加速移动。

unity实现网络对战游戏_ios_25

unity实现网络对战游戏_服务器端_26

unity实现网络对战游戏_服务器端_27

unity实现网络对战游戏_服务器端_28

unity实现网络对战游戏_ios_29

4、课设作品测试

unity实现网络对战游戏_ios_30

unity实现网络对战游戏_服务器端_31

unity实现网络对战游戏_ios_32

unity实现网络对战游戏_客户端_33

unity实现网络对战游戏_客户端_34

unity实现网络对战游戏_客户端_35

unity实现网络对战游戏_服务器端_36

unity实现网络对战游戏_客户端_37

unity实现网络对战游戏_服务器端_38

unity实现网络对战游戏_ios_39

unity实现网络对战游戏_服务器端_40

unity实现网络对战游戏_ios_41

unity实现网络对战游戏_ios_42

到这里一个玩家的测试就结束了,现在测试多个玩家。

unity实现网络对战游戏_服务器端_43

unity实现网络对战游戏_ios_44

unity实现网络对战游戏_客户端_45

unity实现网络对战游戏_服务器端_46

unity实现网络对战游戏_服务器端_47

5、课设总结

这次课设做的时间应该算是最长的了,差不多8月初就开始做了,当然没有一整天的时间去做这个,每天都是在考研复习后开始搞,不过到了后期差不多就全身心搞这个了,整个游戏的客户端难度不大,服务器端难度是最大的,花费时间也是最多的。通过完成这个课设让我的网络游戏开发能力得到了极大的提升,对于我找工作提供了项目支撑,这个项目还有很多地方考研完善的,还能继续改进,继续添加功能,通过一步一步的加大难度自己的收获也越来越多,虽然复杂的也不断上升,代码量不断增加,代码的逻辑越来越复杂,但是,我也还是会不断的去尝试添加更多的功能,让它看上去像一个真正的网络游戏,而不是demo型的,带有各种bug的玩物。项目变大了,也就体会到变量命名的重要性了,知道写好注释的重要性了,写到后面忘了前面,自己前面写了什么,怎么写的都会记不起来了。整个项目明显不是一下子就做出来的,必须通过不断重构,不断迭代才能做好。代码的可扩展性和架构是如此的重要,写代码前不好好搭框架,而是想到什么写什么,后面根本就无法写,最后只能从头再来。相信有了这次课设的基础,能够在未来开发出一款出色的网络游戏。