本文素材和代码全部来自unity asset store里面的3D Infinite Runner Toolkit项目
步骤
场景一:TitleScene
1 搭建开始界面场景
这个场景是开启游戏的第一个界面,用于选择切换到正式游戏场景或者商店。
只需要拖入主场景模型和一个角色模型即可,并添加UI资源。
2 添加角色动画逻辑和场景切换逻辑脚本
将要切换的脚本设置为一个变量名,然后切换场景的脚本就可以在不同button间复用
<span style="white-space:pre"> </span>void OnMouseUpAsButton(){ this.guiTexture.texture = normal; Application.LoadLevel (levelName); }
场景二:Shop
1 搭建商店界面场景
商店场景用于角色选择和购买,当然还可以扩展其他功能,比如购买道具什么的。
也是导入相关模型就行了,添加UI资源。
2 界面交互基本逻辑脚本
触摸(光标滑动)
<span style="white-space:pre"> </span>IEnumerator SelectDirection(){ bool input = false; while (input == false) { if((Input.mousePosition.x - getMousePos.x) < -40){ indexSelect++; if(indexSelect >= players.Length-1){ indexSelect = players.Length-1; } input = true; } if((Input.mousePosition.x - getMousePos.x) > 40){ indexSelect--; if(indexSelect <= 0){ indexSelect = 0; } input = true; } if(Input.GetMouseButtonUp(0)){ input = true; } yield return 0; }
角色移位
<span style="white-space:pre"> </span>IEnumerator MoveToPoint(){ while (Vector3.Distance(transform.position, point[indexSelect]) > 0.01f) { transform.position = Vector3.Lerp(transform.position, point[indexSelect], 10 * Time.deltaTime); yield return 0; } transform.position = point [indexSelect]; StartCoroutine (WaitInput ()); }
场景三:PlayGame
1 搭建游戏初始场景
载入街道等初始模型到场景,调整漫反射光
2 添加游戏角色
给角色添加刚体、碰撞体、控制脚本和动画控制器,然后存为预设体。
角色控制有跳、滑,左移右移还有二段跳,分别重写了键盘控制和触摸控制的接口。
<span style="white-space:pre"> </span>IEnumerator MoveBack(){ float z = transform.position.z-0.5f; bool complete = false; while(complete == false){ transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x,transform.position.y,z),2*Time.deltaTime); if((transform.position.z - z) < 0.05f){ complete = true; } yield return 0; } yield return 0; } private void MoveForward(){ speedMove = GameAttribute.gameAttribute.speed; if(characterController.isGrounded){ moveDir = Vector3.zero; if(directInput == DirectionInput.Up){ Jump(); if(isDoubleJump){ jumpSecond = true; } } }else{ if(directInput == DirectionInput.Down){ QuickGround(); } if(directInput == DirectionInput.Up){ if(jumpSecond){ JumpSeccond(); jumpSecond = false; } } if(animationManager.animationState != animationManager.Jump && animationManager.animationState != animationManager.JumpSecond && animationManager.animationState != animationManager.Roll){ animationManager.animationState = animationManager.JumpLoop; } } moveDir.z = 0; moveDir += this.transform.TransformDirection(Vector3.forward*speedMove); moveDir.y -= gravity * Time.deltaTime; CheckSideCollision (); characterController.Move((moveDir+direction)*Time.deltaTime); }
与障碍物碰撞逻辑。
<span style="white-space:pre"> </span>private void CheckSideCollision(){ if (positionStand == Position.Right) { if((int)characterController.collisionFlags == 5 || characterController.collisionFlags == CollisionFlags.Sides){ if(transform.position.x < 1.75f && checkSideCollision == false){ Debug.Log("Hit"); CameraFollow.instace.ActiveShake(); positionStand = Position.Middle; checkSideCollision = true; } } } if (positionStand == Position.Left) { if((int)characterController.collisionFlags == 5 || characterController.collisionFlags == CollisionFlags.Sides){ if(transform.position.x > -1.75f && checkSideCollision == false){ Debug.Log("Hit"); CameraFollow.instace.ActiveShake(); positionStand = Position.Middle; checkSideCollision = true; } } } if(positionStand == Position.Middle){ if((int)characterController.collisionFlags == 5 || characterController.collisionFlags == CollisionFlags.Sides){ if(transform.position.x < -0.05f && checkSideCollision == false){ Debug.Log("Hit"); CameraFollow.instace.ActiveShake(); positionStand = Position.Left; checkSideCollision = true; }else if(transform.position.x > 0.05f && checkSideCollision == false){ Debug.Log("Hit"); CameraFollow.instace.ActiveShake(); positionStand = Position.Right; checkSideCollision = true; } } } if (checkSideCollision == true) { countDeleyInput += Time.deltaTime; if(countDeleyInput >= 1f){ checkSideCollision = false; countDeleyInput = 0; } } }
金币拾取
<span style="white-space:pre"> </span>public void Magnet(float time){ StopCoroutine("CancleMagnet"); magnet.SetActive(true); timeMagnet = time; StartCoroutine(CancleMagnet()); }
3 创建游戏控制器
添加空物体到场景,挂载脚本,异步加载场景、开始游戏、动态创建游戏物体,得分更新以及重置。
<span style="white-space:pre"> </span>//Loading method IEnumerator WaitLoading(){ while(patSysm.loadingComplete == false){ yield return 0; } StartCoroutine(InitPlayer()); } //Spawn player method IEnumerator InitPlayer(){ GameObject go = (GameObject)Instantiate(playerPref[selectPlayer], posStart, Quaternion.identity); cameraFol.target = go.transform; yield return 0; StartCoroutine(UpdatePerDistance()); } //update distance score IEnumerator UpdatePerDistance(){ while(true){ if(PatternSystem.instance.loadingComplete){ if(GameAttribute.gameAttribute.pause == false && GameAttribute.gameAttribute.isPlaying == true && GameAttribute.gameAttribute.life > 0){ if(Controller.instace.transform.position.z > 0){ GameAttribute.gameAttribute.distance += GameAttribute.gameAttribute.speed * Time.deltaTime; distanceCheck += GameAttribute.gameAttribute.speed * Time.deltaTime; if(distanceCheck >= speedAddEveryDistance){ GameAttribute.gameAttribute.speed += speedAdd; if(GameAttribute.gameAttribute.speed >= speedMax){ GameAttribute.gameAttribute.speed = speedMax; } countAddSpeed++; distanceCheck = 0; } } } } yield return 0; } } //reset game public IEnumerator ResetGame(){ GameAttribute.gameAttribute.isPlaying = false; GUIManager.instance.showSumGUI = true; int oldCoind = GameData.LoadCoin (); GameData.SaveCoin((int)GameAttribute.gameAttribute.coin+oldCoind); distanceCheck = 0; countAddSpeed = 0; yield return 0; }
4 设置摄像机跟随
主摄像机挂载脚本,主要有跟随、重置和晃动逻辑
<span style="white-space:pre"> </span>void LateUpdate(){ if(target != null){ if(target.position.z >= 0){ if(shake == false){ posCamera.x = Mathf.Lerp(posCamera.x, target.position.x, 5 * Time.deltaTime); posCamera.y = Mathf.Lerp(posCamera.y, target.position.y + height, 5 * Time.deltaTime); posCamera.z = Mathf.Lerp(posCamera.z, target.position.z + distance, GameAttribute.gameAttribute.speed); //* Time.deltaTime); transform.position = posCamera; angleCam.x = angle; angleCam.y = Mathf.Lerp(angleCam.y, 0, 1 * Time.deltaTime); angleCam.z = transform.eulerAngles.z; transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, angleCam, 1 * Time.deltaTime); } }else{ if(PatternSystem.instance.loadingComplete == true){ Vector3 dummy = Vector3.zero; posCamera.x = Mathf.Lerp(posCamera.x, 0, 5 * Time.deltaTime); posCamera.y = Mathf.Lerp(posCamera.y, dummy.y + height, 5 * Time.deltaTime); posCamera.z = dummy.z + distance; transform.position = posCamera; angleCam.x = angle; angleCam.y = transform.eulerAngles.y; angleCam.z = transform.eulerAngles.z; transform.eulerAngles = angleCam; } } } } //Reset camera when charater die public void Reset(){ shake = false; Vector3 dummy = Vector3.zero; posCamera.x = 0; posCamera.y = dummy.y + height; posCamera.z = dummy.z + distance; transform.position = posCamera; angleCam.x = angle; angleCam.y = transform.eulerAngles.y; angleCam.z = transform.eulerAngles.z; transform.eulerAngles = angleCam; } //Shake camera public void ActiveShake(){ shake = true; StartCoroutine(ShakeCamera()); }
5 调整场景物体的尺寸
可以用标准立方体作为标尺来衡量场景物体的长宽高,使得游戏角色和场景物体比例得当。
6 添加游戏物体实例管理系统
创建空物体,挂载脚本,用于动态管理场景物体的更新(比如街道的无限延长,障碍物的随机产生等等)
<span style="white-space:pre"> </span>IEnumerator AddBuilding(){ QueueFloor qFloor = queneFloor[0]; queneFloor.RemoveRange(0,1); int i = 0; randomPattern = Random.Range(0, patternBuilding.Count); randomItem = Random.Range(0, patternItem.Count); while(i < building_Script.Count){ int j = 0; while(j < patternBuilding[randomPattern].stateBuilding_Left.Length){ CheckAddBuilding_Left(i,j,qFloor); j++; } j = 0; while(j < patternBuilding[randomPattern].stateBuilding_Right.Length){ CheckAddBuilding_Right(i,j,qFloor); j++; } i++; } yield return 0; i = 0; CheckTypeItemFormAdd(qFloor, i); yield return 0; qFloor.floorObj.transform.position = posFloorLast; posFloorLast.z += nextPosFloor; queneFloor.Add(qFloor); StartCoroutine(WaitCheckFloor()); yield return 0; }
<span style="white-space:pre"> </span>void AddItemWihtType_SubRight(QueueFloor floor, int slotIndex,int type){ if(patternItem[randomItem].itemType_SubRight[slotIndex].x == type){ int j = 0; while(j < amount_Item_Pattern_SubRight[type-1]){ if(j < item_Type_Script[type-1].itemList.Count){ if(item_Type_Script[type-1].itemList[j].itemActive == false && floor.floorItemSlotClass.floor_Slot_SubRight[slotIndex] == false){ SetPosItem_SubRight_For_Type(slotIndex,type-1,j,floor, patternItem[randomItem].itemType_SubRight[slotIndex].y); j = 0; } } j++; } } }
7 设置障碍物摆放
这里用到unity一个非常棒的工具,可以用图形化的手段对内部数据结构进行配置,实现所见即所得
如图,可以手动配置街道物体,接到障碍物,很好的关卡设置编辑器。
编译
将项目编译为可执行文件,需要在build设置里面添加所有scene,注意顺序。
游戏效果
开始界面
商店
游戏中
时间: 2024-08-11 11:06:05