https://github.com/Coding-w-Art/ManGodSkyGroup

某一年年会前,公司组织了一次游戏马拉松活动,要求是在 48H 内做一个可以游玩的游戏,要有原创性,创新点和核心的玩法,还要包含公司的元素。时间段任务重,很快就决定了需要在现有成熟的玩法进行修改。
给项目起名叫男神天团(Man God Sky Group),那就放出来游戏里的男神角色:三个老板 辟邪 镇楼:



游戏的核心玩法就是双人版别踩白块,两个玩家选择角色后,消去一块后,对应的角色向前移动一步,最先移动到重点的玩家获胜。
我的分工是别踩白块的游戏逻辑和界面表现部分。其他还有角色设计、角色移动表现、结算、联机功能、排行榜等由其他人负责。由于是现有项目魔改,所以使用了现有项目的服务器,新添加了消息进行双方进度的同步。
砖块的核心逻辑:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class UIBlock : MonoBehaviour
{
public UILabel m_kSkillCount;
public UITexture[] m_kTexLine_0;
public UITexture[] m_kTexLine_1;
public UITexture[] m_kTexLine_2;
public UITexture[] m_kTexLine_3;
public UITexture[] m_kTexLine_4;
public UITexture[] m_kTexLine_5;
public UITexture[] m_kTexLine_6;
public UITexture[] m_kTexLine_7;
public UITexture[] m_kTexLine_8;
public GameObject m_kLine_0;
public GameObject m_kLine_1;
public GameObject m_kLine_2;
public GameObject m_kLine_3;
public GameObject m_kLine_4;
public GameObject m_kLine_5;
public GameObject m_kLine_6;
public GameObject m_kLine_7;
public GameObject m_kLine_8;
public UIMain m_kMainUI;
public UISprite m_kComboProgress;
public GameObject m_kTexSkill;
public int[] Key = { (int)KeyCode.Q, (int)KeyCode.W, (int)KeyCode.E };
public float fSpeedRate = 1f;
private const int MAX_LINE_COUNT = 9;
private const int MAX_ROW_COUNT = 3;
private const int BLOCK_SIZE = 60;
private const int BLOCK_TOTAL_SIZE = 480;
float fMoveDelta = 0;
int iRollCount = 0;
int iCurrentLine = 0;
bool[,] bData = new bool[MAX_LINE_COUNT, MAX_ROW_COUNT];
bool bGameStart = false;
float fStartTime = 0f;
float fDropTime = 0.05f;
public List<GameObject> m_kTexList;
public List<UITexture[]> m_kTexlinelist;
float[] fOriginY = new float[MAX_LINE_COUNT];
int iComboCount = 0;
bool bBlockDroping = false;
bool bSkillSlow = false;
bool bSkillSlowCounter = false;
public int iPanelIndex = 0;
public int iPlayerIndex = 0;
public int iOtherPlayerIndex = 0;
private int MaxStepCount = 155;
private float fSkillDuration = 2f;
private float fWrongDuration = 0.3f;
private float fSkill1Duration = 1.5f;
private float fSkill2Duration = 1.5f;
private float fSkill3Duration = 6f;
private int[] iSkill1Combo = {20, 15, 25, 20 };
private AUDIO_TYPE audioType;
public List<GameObject> m_kBtns = new List<GameObject>();
public class kBlock
{
public bool bBanana;
public UITexture kTex;
}
public kBlock[,] m_kTexlinelistNew = new kBlock[MAX_LINE_COUNT, MAX_ROW_COUNT];
// Use this for initialization
void Start()
{
MaxStepCount = m_kMainUI.MaxStepCount;
m_kTexlinelistNew[0, 0] = new kBlock();
m_kTexlinelist = new List<UITexture[]>
{
m_kTexLine_0, m_kTexLine_1, m_kTexLine_2,
m_kTexLine_3, m_kTexLine_4, m_kTexLine_5,
m_kTexLine_6, m_kTexLine_7, m_kTexLine_8,
};
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
for (int j = 0; j < MAX_ROW_COUNT; j++)
{
m_kTexlinelistNew[i, j] = new kBlock();
m_kTexlinelistNew[i, j].kTex = m_kTexlinelist[i][j];
m_kTexlinelistNew[i, j].kTex.mainTexture = Resources.Load("UI/Main/tex_block_0") as Texture2D;
m_kTexlinelistNew[i, j].bBanana = false;
}
}
m_kTexList = new List<GameObject>
{
m_kLine_0, m_kLine_1, m_kLine_2,
m_kLine_3, m_kLine_4, m_kLine_5,
m_kLine_6, m_kLine_7, m_kLine_8,
};
audioType = (iPanelIndex == 0) ? AUDIO_TYPE.AT_Guild : AUDIO_TYPE.AT_Event;
InitBlockData();
UpdateComboProgress();
}
// Update is called once per frame
void Update()
{
if (!bGameStart)
return;
if (bBlockDroping)
return;
if (Input.GetKeyDown((KeyCode)Key[0]))
{
GetKeyResult(0);
}
if (Input.GetKeyDown((KeyCode)Key[1]))
{
GetKeyResult(1);
}
if (Input.GetKeyDown((KeyCode)Key[2]))
{
GetKeyResult(2);
}
}
void onKeyClick(GameObject go)
{
if (bBlockDroping)
return;
GetKeyResult(m_kBtns.IndexOf(go));
}
public void InitNativePlayerKey()
{
foreach (GameObject btn in m_kBtns)
{
UIEventListener.Get(btn).onClick += onKeyClick;
}
}
IEnumerator BlockDrop()
{
fStartTime = Time.time;
float fPercent = 0f;
while (fPercent < 1)
{
fPercent = (Time.time - fStartTime) / fDropTime;
fPercent = fPercent > 1 ? 1 : fPercent;
fMoveDelta = fPercent * BLOCK_SIZE;
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
m_kTexList[i].transform.localPosition = new Vector3(0, fOriginY[i] - fMoveDelta, 0);
}
yield return null;
}
UpdateOneLineBlock(iCurrentLine);
m_kTexList[iCurrentLine].transform.localPosition = new Vector3(0, BLOCK_TOTAL_SIZE, 0);
iRollCount++;
iCurrentLine++;
iCurrentLine = iCurrentLine < MAX_LINE_COUNT ? iCurrentLine : 0;
bBlockDroping = false;
}
public void SetGameStart()
{
bGameStart = true;
}
public void SetGameStop()
{
bGameStart = false;
}
void InitBlockData()
{
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
int j = GetRandom();
m_kTexlinelistNew[i, j].bBanana = true;
m_kTexlinelistNew[i, j].kTex.GetComponent<UITexture>().mainTexture = Resources.Load("UI/Main/tex_block_1") as Texture2D;
}
}
void SetUITexture(UITexture kTexture, bool bData)
{
string strName = bData ? "UI/Main/tex_block_1" : "UI/Main/tex_block_0";
var tex = Resources.Load(strName) as Texture2D;
kTexture.mainTexture = tex;
}
void UpdateOneLineBlock(int iNewLine)
{
m_kTexlinelistNew[iNewLine, 0].bBanana = false;
m_kTexlinelistNew[iNewLine, 0].kTex.mainTexture = Resources.Load("UI/Main/tex_block_0") as Texture2D;
m_kTexlinelistNew[iNewLine, 1].bBanana = false;
m_kTexlinelistNew[iNewLine, 1].kTex.mainTexture = Resources.Load("UI/Main/tex_block_0") as Texture2D;
m_kTexlinelistNew[iNewLine, 2].bBanana = false;
m_kTexlinelistNew[iNewLine, 2].kTex.mainTexture = Resources.Load("UI/Main/tex_block_0") as Texture2D;
int i = GetRandom();
m_kTexlinelistNew[iNewLine, i].bBanana = true;
m_kTexlinelistNew[iNewLine, i].kTex.mainTexture = Resources.Load("UI/Main/tex_block_1") as Texture2D;
}
void GetKeyResult(int iInput)
{
if (!bGameStart)
return;
if(m_kTexlinelistNew[iCurrentLine, iInput].bBanana)
{
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
fOriginY[i] = m_kTexList[i].transform.localPosition.y;
}
bBlockDroping = true;
m_kTexlinelistNew[iCurrentLine, iInput].kTex.mainTexture = Resources.Load("UI/Main/tex_block_3") as Texture2D;
iComboCount++;
AudioPlayer.Instance().Play(audioType, @"Audio/Correct", false);
StartCoroutine(BlockDrop());
if (bSkillSlow)
{
if(bSkillSlowCounter)
{
SYSLogic.Instance().IncreasePlayerStep(iPanelIndex);
}
bSkillSlowCounter = !bSkillSlowCounter;
}
else
{
SYSLogic.Instance().IncreasePlayerStep(iPanelIndex);
}
if (SYSLogic.Instance().GetPlayerStep(iPanelIndex) >= MaxStepCount)
{
//===================================
//TODO:游戏结束
m_kMainUI.SetGameStop();
bGameStart = false;
//===================================
}
}
else
{
bBlockDroping = true;
iComboCount = 0;
m_kTexlinelistNew[iCurrentLine, iInput].kTex.mainTexture = Resources.Load("UI/Main/tex_block_2") as Texture2D;
AudioPlayer.Instance().Play(audioType, @"Audio/Error", false);
StartCoroutine(WrongInput());
}
UpdateComboProgress();
if (iComboCount >= iSkill1Combo[iPlayerIndex])
{
iComboCount = 0;
m_kMainUI.CastSkill(iPanelIndex, iPlayerIndex);
}
}
void UpdateComboProgress()
{
m_kComboProgress.fillAmount = (float)iComboCount / (float)iSkill1Combo[iPlayerIndex];
}
int GetRandom()
{
int i = Random.Range(0, 3);
return i % MAX_ROW_COUNT;
}
public void CastSkill(int iPlayer)
{
switch (iPlayer)
{
case 1:
StartCoroutine(CastSkill1());
break;
case 2:
StartCoroutine(CastSkill2());
break;
case 3:
StartCoroutine(CastSkill3());
break;
default:
break;
}
}
IEnumerator WrongInput()
{
float fTime = Time.time;
bGameStart = false;
while (Time.time < fTime + fWrongDuration)
{
yield return null;
}
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
fOriginY[i] = m_kTexList[i].transform.localPosition.y;
}
StartCoroutine(BlockDrop());
bGameStart = true;
}
//遮挡
IEnumerator CastSkill1()
{
float fTime = Time.time;
m_kTexSkill.SetActive(true);
while (Time.time < fTime + fSkill1Duration)
{
yield return null;
}
m_kTexSkill.SetActive(false);
}
//停止
IEnumerator CastSkill2()
{
float fTime = Time.time;
bGameStart = false;
SetBananaGrey();
while (Time.time < fTime + fSkill2Duration)
{
yield return null;
}
SetBananaNormal();
bGameStart = true;
}
//减速
IEnumerator CastSkill3()
{
float fTime = Time.time;
SetBananaBad();
bSkillSlow = true;
while (Time.time < fTime + fSkill3Duration)
{
yield return null;
}
SetBananaNormal();
bSkillSlow = false;
}
void SetBananaGrey()
{
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
for (int j = 0; j < MAX_ROW_COUNT; j++)
{
if (m_kTexlinelistNew[i, j].bBanana)
m_kTexlinelistNew[i, j].kTex.mainTexture = Resources.Load("UI/Main/tex_xiangjiao_1") as Texture2D;
}
}
}
void SetBananaNormal()
{
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
for (int j = 0; j < MAX_ROW_COUNT; j++)
{
if (m_kTexlinelistNew[i, j].bBanana)
m_kTexlinelistNew[i, j].kTex.mainTexture = Resources.Load("UI/Main/tex_block_1") as Texture2D;
}
}
}
void SetBananaBad()
{
for (int i = 0; i < MAX_LINE_COUNT; i++)
{
for (int j = 0; j < MAX_ROW_COUNT; j++)
{
if (m_kTexlinelistNew[i, j].bBanana)
m_kTexlinelistNew[i, j].kTex.mainTexture = Resources.Load("UI/Main/tex_xiangjiao_2") as Texture2D;
}
}
}
}
游戏主流程的核心逻辑:
using UnityEngine;
using System.Collections;
using System;
public class UIMain : MonoBehaviour {
public Transform m_kLeftAnchor;
public Transform m_kRightAnchor;
public int MaxStepCount = 120;
public UITexture m_kTexAvatar1;
public UITexture m_kTexAvatar2;
public UILabel m_kLabPlayerName1;
public UILabel m_kLabPlayerName2;
public UILabel m_kLabSkillNum;
public UIBlock m_kLeftBlockPanel;
public UIBlock m_kRightBlockPanel;
public GameObject m_kPlayer1;
public GameObject m_kPlayer2;
public GameObject m_kSmallAvatar_1;
public GameObject m_kSmallAvatar_2;
public UISprite m_kProgress;
public GameObject m_kTimePanel;
public UILabel m_kLabTime;
public GameObject m_kStartPanel;
public UIFinish m_kFinishPanel;
public GameObject m_kSkillPanel;
public UITexture m_kTexSkill;
public GameObject[] m_kStartCounter;
private CharacterControl m_kPlayer1Control;
private CharacterControl m_kPlayer2Control;
public int iPlayer1 = 0;
public int iPlayer2 = 1;
public Vector3 m_kPlayer1StartPos = new Vector3(0f,0f,0f);
public Vector3 m_kPlayer2StartPos = new Vector3(0f, 0f, 0f);
public Transform m_kMainCamera;
public Transform m_kLight;
bool bIsGameStart;
public Transform m_kForaneTex;
public float m_kForaneFollowCameraRate = 0.5f;
private Vector3 mLastFrameCameraPos;
private float fStartTime;
private float fAvatarPosOrigin = -200f;
private float fAvatarPosMove = 400f;
private string[] strTexSkillPath = { "", "tex_weifeng_1", "tex_heiyi_1", "tex_bingge_1" };
void Awake()
{
adaptScreen();
}
private void adaptScreen()
{
Vector3 rightAnchorPos = m_kRightAnchor.transform.localPosition;
rightAnchorPos.x += UIUtility.GetDockOffsetX();
m_kRightAnchor.transform.localPosition = rightAnchorPos;
Vector3 leftAnchorPos = m_kLeftAnchor.transform.localPosition;
leftAnchorPos.x -= UIUtility.GetDockOffsetX();
m_kLeftAnchor.transform.localPosition = leftAnchorPos;
}
// Use this for initialization
void Start () {
m_kLeftBlockPanel.m_kMainUI = this;
m_kRightBlockPanel.m_kMainUI = this;
iPlayer1 = SYSLogic.Instance().GetPlayerInfo(0);
iPlayer2 = SYSLogic.Instance().GetPlayerInfo(1);
SYSLogic.Instance().ClearPlayerStep();
SetKey();
SetAvatar();
m_kPlayer1 = ResourceMgr.LoadResources(@"Character/Character0" + iPlayer1 + "_Prefab");
m_kPlayer1.animation.Play("Player1DropDown");
m_kPlayer2 = ResourceMgr.LoadResources(@"Character/Character0" + iPlayer2 + "_Prefab");
m_kPlayer2.animation.Play("Player2DropDown");
m_kPlayer1Control = m_kPlayer1.GetComponent<CharacterControl>();
m_kPlayer2Control = m_kPlayer2.GetComponent<CharacterControl>();
m_kPlayer1Control.Init(0);
m_kPlayer2Control.Init(1);
m_kPlayer1.transform.position = m_kPlayer1StartPos;
m_kPlayer2.transform.position = m_kPlayer2StartPos;
Vector3 cameraPos = m_kMainCamera.position;
//cameraPos.x = ((m_kPlayer1StartPos + m_kPlayer2StartPos) / 2).x;
if (m_kPlayer1.transform.position.x < m_kPlayer2.transform.position.x)
{
cameraPos.x = m_kPlayer1.transform.position.x;
}
else
{
cameraPos.x = m_kPlayer2.transform.position.x;
}
m_kMainCamera.position = cameraPos;
mLastFrameCameraPos = cameraPos;
m_kProgress.fillAmount = 0f;
StartCoroutine(StartCounter());
//SetGameStart();
}
IEnumerator StartCounter()
{
fStartTime = Time.time;
while (Time.time < fStartTime + 1f)
{
yield return null;
}
fStartTime = Time.time;
m_kStartPanel.SetActive(true);
m_kStartCounter[4].SetActive(true);
AudioPlayer.Instance().Play(AUDIO_TYPE.AT_UI_BGM, @"Audio/Counter", false);
while (Time.time < fStartTime + 1f)
{
yield return null;
}
fStartTime = Time.time;
m_kStartCounter[4].SetActive(false);
m_kStartCounter[3].SetActive(true);
while (Time.time < fStartTime + 1f)
{
yield return null;
}
fStartTime = Time.time;
m_kStartCounter[3].SetActive(false);
m_kStartCounter[2].SetActive(true);
while (Time.time < fStartTime + 1f)
{
yield return null;
}
fStartTime = Time.time;
m_kStartCounter[2].SetActive(false);
m_kStartCounter[1].SetActive(true);
while (Time.time < fStartTime + 1f)
{
yield return null;
}
fStartTime = Time.time;
m_kStartCounter[1].SetActive(false);
m_kStartCounter[0].SetActive(true);
AudioPlayer.Instance().Play(AUDIO_TYPE.AT_SCENE_BGM, @"Audio/RunningBGM", true);
while (Time.time < fStartTime + 1f)
{
yield return null;
}
m_kStartCounter[0].SetActive(false);
m_kStartPanel.SetActive(false);
SetGameStart();
}
void SetKey()
{
m_kLeftBlockPanel.Key = new int[] { (int)KeyCode.A, (int)KeyCode.S, (int)KeyCode.D };
m_kRightBlockPanel.Key = new int[] { (int)KeyCode.LeftArrow, (int)KeyCode.DownArrow, (int)KeyCode.RightArrow };
}
void SetAvatar()
{
m_kLeftBlockPanel.iPanelIndex = 0;
m_kRightBlockPanel.iPanelIndex = 1;
m_kLeftBlockPanel.iPlayerIndex = iPlayer1;
m_kLeftBlockPanel.iOtherPlayerIndex = iPlayer2;
m_kRightBlockPanel.iPlayerIndex = iPlayer2;
m_kRightBlockPanel.iOtherPlayerIndex = iPlayer1;
m_kTexAvatar1.mainTexture = Resources.Load("UI/Main/tex_avatar_" + iPlayer1) as Texture2D;
m_kTexAvatar2.mainTexture = Resources.Load("UI/Main/tex_avatar_" + iPlayer2) as Texture2D;
m_kSmallAvatar_1.GetComponent<UITexture>().mainTexture = Resources.Load("UI/Main/tex_avatar_" + iPlayer1) as Texture2D;
m_kSmallAvatar_2.GetComponent<UITexture>().mainTexture = Resources.Load("UI/Main/tex_avatar_" + iPlayer2) as Texture2D;
m_kLeftBlockPanel.InitNativePlayerKey();
m_kRightBlockPanel.InitNativePlayerKey();
}
// Update is called once per frame
void Update () {
m_kLight.LookAt(m_kMainCamera);
Vector3 cameraPos = m_kMainCamera.position;
float displacement = cameraPos.x - mLastFrameCameraPos.x;
mLastFrameCameraPos = cameraPos;
mLastFrameCameraPos = m_kMainCamera.position;
if (m_kPlayer1.transform.position.x < m_kPlayer2.transform.position.x)
{
cameraPos.x = m_kPlayer1.transform.position.x;
}
else
{
cameraPos.x = m_kPlayer2.transform.position.x;
}
//cameraPos.x = ((m_kPlayer1.transform.position + m_kPlayer2.transform.position) / 2).x;
m_kMainCamera.position = cameraPos;
Vector3 foraneTexPos = m_kForaneTex.position;
foraneTexPos.x += displacement * m_kForaneFollowCameraRate;
m_kForaneTex.position = foraneTexPos;
if(bIsGameStart)
{
UpdateTime();
float fProgress = (float)GetMaxStep() / (float)MaxStepCount;
m_kProgress.fillAmount = fProgress > 1 ? 1 : fProgress;
}
}
void UpdateTime()
{
float fDeltaTime = Time.time - fStartTime;
TimeSpan ts = new TimeSpan(0, 0, 0, (int)fDeltaTime);
string strMilliSecond = ((fDeltaTime - (int)fDeltaTime).ToString() + "00").Substring(2, 2);
m_kLabTime.text = ts.Minutes.ToString() + ":" + ts.Seconds.ToString() + "." + strMilliSecond;
}
int GetMaxStep()
{
int iPlayer1 = SYSLogic.Instance().GetPlayerStep(0);
int iPlayer2 = SYSLogic.Instance().GetPlayerStep(1);
m_kSmallAvatar_1.transform.localPosition = new Vector3(fAvatarPosOrigin + ((float)iPlayer1 / (float)MaxStepCount) * fAvatarPosMove, -16, 0);
m_kSmallAvatar_2.transform.localPosition = new Vector3(fAvatarPosOrigin + ((float)iPlayer2 / (float)MaxStepCount) * fAvatarPosMove, -56, 0);
return iPlayer1 > iPlayer2 ? iPlayer1 : iPlayer2;
}
public void SetGameStart()
{
bIsGameStart = true;
m_kLeftBlockPanel.SetGameStart();
if (!SYSLogic.Instance().m_bPVP) m_kRightBlockPanel.SetGameStart();
m_kTimePanel.SetActive(true);
fStartTime = Time.time;
}
public void SetGameStop()
{
bIsGameStart = false;
m_kLeftBlockPanel.SetGameStop();
m_kRightBlockPanel.SetGameStop();
bool bPlayer1 = (SYSLogic.Instance().GetPlayerStep(0) > SYSLogic.Instance().GetPlayerStep(1));
SYSLogic.Instance().FinishBattle((int)(Time.time - fStartTime));
m_kFinishPanel.SetData(bPlayer1, iPlayer1, iPlayer2);
m_kFinishPanel.gameObject.SetActive(true);
AudioPlayer.Instance().Pause(AUDIO_TYPE.AT_SCENE_BGM);
AudioPlayer.Instance().Play(AUDIO_TYPE.AT_Use_Once, @"Audio/Victory", false);
}
public void CastSkill(int iPanelIndex, int iPlayerIndex)
{
AudioPlayer.Instance().Play(AUDIO_TYPE.AT_Use_Once, @"Audio/Skill", false);
StartCoroutine(ShowSkillPanel(iPlayerIndex));
switch (iPanelIndex)
{
case 0:
m_kRightBlockPanel.CastSkill(iPlayerIndex);
break;
case 1:
m_kLeftBlockPanel.CastSkill(iPlayerIndex);
break;
default:
break;
}
}
IEnumerator ShowSkillPanel(int iPlayer)
{
float fStartTime = Time.time;
m_kTexSkill.mainTexture = Resources.Load("UI/Main/" + strTexSkillPath[iPlayer]) as Texture2D;
m_kSkillPanel.SetActive(true);
while (Time.time < fStartTime + 1f)
{
yield return null;
}
m_kSkillPanel.SetActive(false);
}
public void playStartCountdownVoice()
{
AudioSource _audioSource = new AudioSource();
}
}