fffff (jingangxin36/FFFFFlipping) | MoltPulseBack to Directoryfffff
3D像素跑酷游戏----放荡不羁的FFFFFlipping Boy
MoltPulse
Based on repository activity, growth velocity and community engagement.
24
FFFFFlipping
1. 游戏截图
[待补充]>>>emm录制gif的时候出错了...
2. APK下载
FFFFFlipping-V1.1.apk
3. 怎么玩?
- 点键help按钮, 会显示操作提示
- 你可以向左跳, 向右跳, 每次只能跳一步
- 你可以向后跳一步来调整战略, 防止陷入僵局, 或者踩爆更多的敌人!
- 你可以收集金币, 然后打破纪录! (但是现在还没有商店)
- 踩爆敌人获得加血, 踩到陷阱☠则会死亡
- 长时间没有加血, 呼吸和速度都会变慢! 然后死掉!
- 开始弹跳之旅吧
4. 开发环境
5. 技术关键点解析和拓展
5.1 碰撞检测
项目中的使用场景
- 无限地图的拼接: 通过在相机处设置碰撞体, 当相机看不见某一行block时, 将该block移到最后一排, 实现无限地图
- cell(金币/敌人/陷阱☠)的处理: 主角跳到敌人或陷阱的cell, 会有不同的反馈
拓展:
5.2 相机跟随
效果图:

实现细节:
关键代码如下:
using UnityEngine;
public class CameraMovement : MonoBehaviour {
public GameObject followTarget;
public float moveSpeed;
void Update() {
if (followTarget != null) {
//相机位置Z值与目标点的Z值做插值, 实现相机前后跟随, 而目标点运动不影响
var newZ = Mathf.Lerp(transform.position.z, followTarget.transform.position.z, Time.deltaTime * moveSpeed);
var newVector3 = new Vector3(transform.position.x, transform.position.y, newZ);
transform.position = newVector3;
}
}
}
5.3 无限地图拼接
效果图:

using UnityEngine;
public class BlockMovement : MonoBehaviour {
void OnTriggerExit(Collider other) {
//当相机看不见某一行block时, 将该block移到最后一排, 实现无限地图
if (other.gameObject.tag == "Floor") {
BlockManager.Instance.ChangeBlockPosition(other.transform);
}
if (other.gameObject.tag == "PoolItem") {
var poolItem = other.transform.GetComponent<Cell>();
poolItem.ReleaseIntoPool();
}
}
}
5.4 敌人视觉模拟和角度跟随
怎样算看到呢? 项目中, 主角与敌人的z值距离小于指定值时, 可以断定主角进入敌人的视觉范围
要怎么转向主角呢? 用四元数来表示旋转, 同时限制欧拉角中的y值, 来限制旋转角度
protected virtual bool SeesPlayer =>
((Player != null) && (Mathf.Abs((int)(transform.position.z - Player.transform.position.z)) <= visionRange));
//...
protected virtual void Update() {
if (Player != null) {
if (SeesPlayer) TurnTowardPlayer();
}
}
//...
private void TurnTowardPlayer() {
Vector3 position = Player.transform.position;
//忽略y值的影响
position.y = transform.position.y;
Vector3 forward = transform.position - position;
if (forward != Vector3.zero) {
Quaternion b = Quaternion.LookRotation(forward);
transform.rotation = Quaternion.Lerp(transform.rotation, b, Time.deltaTime * turnRate);
float y = transform.eulerAngles.y;
//确定旋转角度
if (y > 180f) y -= 360f;
//将旋转角度限制在-45度和+45度之间
transform.rotation = Quaternion.Euler(0f, Mathf.Clamp(y, -mLookLimit, mLookLimit), 0f);
}
}
- 3D数学中表现旋转的方式有:
- 旋转矩阵: 行列式为1的正交矩阵
- 旋转矩阵有9个量,但是一次旋转只有3个自由度,因此这种表达方式是冗余的
- 旋转矩阵自身带有约束:它必须是个正交矩阵,且行列式为1
- 欧拉角: 最直观的旋转描述方式,也是一个3维向量,分别代表绕某个轴的旋转角度
- 相同的角度,旋转次序的不同,旋转结果不一样。一般常见的是rpy角(旋转顺序是ZYX)
- 最大的缺点是万向锁问题:俯仰角为±90度时,第一次旋转和第三次旋转将使用同一个轴,使得系统丢失了一个自由度
- 无法实现球面平滑插值
- 四元数: 四元数就是一个高阶复数,也就是一个四维空间
- 它们结构紧凑
- 不受万向节锁定的影响
- 可提供球面平滑插值
- 不够直观
- Unity内部使用四元数来表示所有旋转
- 参考:
5.5 主角跳跃穿越效果
/// <summary>
/// 左右跳时, 主角也向前跳
/// </summary>
/// <param name="direction">-1表示向左跳, 1表示向右跳</param>
private void ForwardJump(int direction) {
mCurrentX += direction;
if (direction == -1) {
//越界了
if (mCurrentX < 0) {
mCurrentX = 2;
var newVector3 = transform.position;
newVector3.x = 2 * characterPositionOffset;
transform.position = newVector3;
}
transform.Translate(Vector3.left * characterPositionOffset);
}
if (direction == 1) {
//越界了
if (mCurrentX > 2) {
mCurrentX = 0;
var newVector3 = transform.position;
newVector3.x = -2 * characterPositionOffset;
transform.position = newVector3;
}
transform.Translate(Vector3.right * characterPositionOffset);
}
JumpForward();
}
5.6 血量不足时的减速处理
public void LowBlood() {
Time.timeScale = 0.5f;
lowBloodView.SetActive(true);
}
public void ResumeNormalBlood() {
Time.timeScale = 1;
lowBloodView.SetActive(false);
}
5.7 重载场景后灯光变暗问题处理
原因:
- 选择的光照是GI realtime实时光照,编辑器在当前场景时,它的灯光是已经渲染好了,但重新加载的时候灯光没有进行渲染
解决方法:
- Window->lighting->settings->右下角取消勾选auto,这时候是没有烘焙灯光的情形,重新加载场景后不再会变暗。
- 如果需要烘培灯光,则点击Generate按钮即可,这时候将保存光照贴图信息,重新加载后也不会再变暗。
解决方法参考:
6. 哪些还可以做得更好
敌人AI, 项目当中暂时没有实现AI功能. 后期有机会我将会补上.
7. 为什么叫做FFFFFlipping呢?
8. 个人博客
9. 项目地址
Ecosystem Role
Standard MoltPulse indexed agent.
Embed Badge
Show off your Pulse Score in your GitHub README to build trust and rank higher.
[](https://molt-pulse.com/agents/jingangxin36/FFFFFlipping)