一、效果图
二、效果制作
我们在unity中用一个点作为父物体,在父物体下创建几张图片,我们旋转父物体的Z轴,就会发现子物体会围绕父物体旋转,通过这个特性,有了一个思路,创建一个围绕一个点的图片圆环,然后通过旋转父物体z轴就可以实现这个效果。好的我们先按照这个思路创建一个图片圆环?
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
/// <summary>
/// 创建圆环
/// </summary>
public class CreateCircle : MonoBehaviour
{
[Header("设置生成的数量")]
[SerializeField] int iconCount = 30;
[Header("需要生成的预设")]
[SerializeField] GameObject prefab;
[Header("设置中心点,同样也是父物体 ")]
[SerializeField] Transform originObj;
[Header("设置圆半径物体")]
[SerializeField] Transform radiusObj;
//圆半径
float fRadius = 500;
void Start()
{
//计算圆半径
fRadius = Vector3.Distance(originObj.position, radiusObj.position);
CreatCircle(); //创建圆环
}
#region 围绕一个点生成圆
int childCount; //孩子数量
public void CreatCircle()
{
childCount = originObj.childCount;
float angle = 360f / iconCount; //计算每个物体的角度
for (int i = 0; i < iconCount; i++)
{
GameObject go = GetPrefab(i);
//sin cos求x,y值
float x = fRadius * Mathf.Sin((angle * i) * (Mathf.PI / 180f));
float y = fRadius * Mathf.Cos((angle * i) * (Mathf.PI / 180f));
go.transform.localPosition = new Vector3(x, y, 0);
go.transform.localEulerAngles = new Vector3(0, 0, Mathf.Abs(angle * i - 360));
go.name = i.ToString();
}
}
//获取预设
GameObject GetPrefab(int index)
{
GameObject obj = null;
if (index < childCount)
obj = originObj.GetChild(index).gameObject;
else
obj = Instantiate(prefab, originObj);
obj.SetActive(true);
return obj;
}
#endregion
}
?界面布局:
?好的下面让我们来看看效果:
?但是我们的圆只需要展示部分图片,所以我们需要把多余的图片删除并将这个圆环拖拽成预设。留着备用,但是问题来了,如果我们一开始盲猜圆的大小、图片的数量来创建圆环往往是不精确,这样的话做出来的圆并不是我们想要的,所以要制作一个我们想要的圆环,我这里优化了一下,代码中有解释,这里就不多说了。
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if UNITY_EDITOR
//窗口编辑脚本,用于创建属性面板的创建、保存预设按钮
[CustomEditor(typeof(CreateCircle))]
public class CreateCircleEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
CreateCircle myScript = (CreateCircle)target;
//创建按钮
if (GUILayout.Button("Create"))
{
myScript.CreatCircle();
}
//保存预设按钮
if (GUILayout.Button("Save"))
{
myScript.SavePrefab();
}
}
}
#endif
/// <summary>
/// 创建圆环
/// </summary>
public class CreateCircle : MonoBehaviour
{
[Header("生成的数量")]
[SerializeField] int iconCount = 30;
[Header("需要生成的预设")]
[SerializeField] GameObject prefab;
[Header("设置中心点,同样也是父物体 ")]
[SerializeField] Transform originObj;
[Header("设置圆半径物体")]
[SerializeField] Transform radiusObj;
[Header("设置保存预设路径")]
[SerializeField] string savePath = "Assets/06_Prefabs";
//圆半径
float fRadius;
void Start()
{
}
#region 围绕一个点生成圆
bool isSuccessful; //是否成功
int childCount; //孩子数量
public void CreatCircle()
{
fRadius = Vector3.Distance(originObj.position, radiusObj.position); //计算圆半径
DestroyPrefab(); //销毁多余预设
childCount = originObj.childCount;
float angle = 360f / iconCount; //计算每个物体的角度
for (int i = 0; i < iconCount; i++)
{
GameObject go = GetPrefab(i);
//sin cos求x,y值
float x = fRadius * Mathf.Sin((angle * i) * (Mathf.PI / 180f));
float y = fRadius * Mathf.Cos((angle * i) * (Mathf.PI / 180f));
go.transform.localPosition = new Vector3(x, y, 0);
go.transform.localEulerAngles = new Vector3(0, 0, Mathf.Abs(angle * i - 360));
go.name = i.ToString();
}
}
//销毁多余预设
void DestroyPrefab()
{
int count = originObj.childCount - iconCount;
for (int i = 0; i < count; i++)
{
Debug.Log(count);
DestroyImmediate(originObj.GetChild(i).gameObject); //使用立即销毁方法,如果使用Destroy会有延迟导致创建不成功
}
}
//获取预设
GameObject GetPrefab(int index)
{
GameObject obj = null;
if (index < childCount)
obj = originObj.GetChild(index).gameObject;
else
obj = Instantiate(prefab, originObj);
obj.SetActive(true);
return obj;
}
#endregion
#region 保存预设
public void SavePrefab()
{
//判断路径是否存在
if (!File.Exists(savePath))
Directory.CreateDirectory(savePath);
#if UNITY_EDITOR
PrefabUtility.SaveAsPrefabAsset(originObj.gameObject, savePath + "/Circle.prefab", out isSuccessful);
#endif
Debug.Log("是否成功" + isSuccessful);
}
#endregion
}
修改后的效果:
?现在我们可以灵活的创建圆环了,我们圆环能看到的其实只有一部分,另一部分的圆环看不到就可以删除了,删除后我们给圆环从左到右排列一下,因为生成的圆是以终点为中心,往左右创建的对称半圆,所以根据如下操作可以快速排列:
?做完这些后按下save保存按钮就可以自动创建这个半圆预设了。
?好的我们的半圆预设创建完成,那么接下来就该实现图片的轮播效果了,我们的轮播效果是当半圆最左边(或最右边)的图片,超出了我们的视野范围内后,就修改位置和旋转到最右边(或最左边,也就是半环的屁股后面),同时判断图片精灵列表中的图片是否都显示过,如果没有就修改图片精灵,这样形成一个循环的过程。
做这个最难点就是如果判断圆环超出了我们的视野范围呢?,其实我们在创建圆环的时候就知道了圆环每旋转多少度就生成一张图片,我上面创建了一个55张图片的圆环,所以圆环每移动6.545度(360度/55张图片约等于6.545度)就代表移动到了下一张图片的位置,这时候根据圆环是向左(或向右)转来修改第一张图片还是最后一张图片的位置。就可以了。这么说有点抽象,我们下面开始做。
1.首先记录一下左右两张图片的位置,这两张图片直接从圆环中复制出一个就可以了,布局如下
?2.脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
/// <summary>
/// 圆环轮播
/// </summary>
public class RoundRobin : MonoBehaviour
{
//计算方式:360°/图片数量
float angle;
[SerializeField] float speed = 10f;
[SerializeField] Transform start;
[SerializeField] Transform end;
[SerializeField] List<Sprite> spriteList; //存放的图片精灵
void Start()
{
angle = 360f / 55f;
//初始化已有的图片
for (int i = 0; i < transform.childCount; i++)
SetImgSprite(1, transform.GetChild(i).GetComponent<Image>());
}
float oldRotX;
void Update()
{
float h = Input.GetAxis("Horizontal");
if (h != 0)
{
float rotX = -(h * speed * Time.deltaTime);
transform.rotation *= Quaternion.Euler(transform.rotation.x, transform.rotation.y, rotX);
//物体 位置 旋转 孩子下标
Transform obj;
Vector3 pos;
Vector3 rot;
int childIndex;
int spriteIndex; //精灵下标 -1或1
//超出或等于角度
if (Mathf.Abs(oldRotX - transform.eulerAngles.z) >= angle)
{
if (rotX > 0)
{
spriteIndex = 1;
//获取物体 位置 旋转 要修改的下标
obj = transform.GetChild(0);
pos = transform.InverseTransformPoint(end.position);
rot = end.eulerAngles - transform.eulerAngles;
childIndex = transform.childCount - 1;
}
else
{
spriteIndex = -1;
//获取物体 位置 旋转 要修改的下标
obj = transform.GetChild(transform.childCount - 1);
pos = transform.InverseTransformPoint(start.position);
rot = start.eulerAngles - transform.eulerAngles;
childIndex = 0;
}
//设置位置 旋转 子物体在父物体的排列位置
obj.localPosition = pos;
obj.localEulerAngles = rot;
obj.SetSiblingIndex(childIndex);
SetImgSprite(spriteIndex, obj.GetComponent<Image>()); //图片修改
oldRotX = transform.eulerAngles.z;
}
}
}
#region 修改图片精灵
int spritIndex = 0;//图片精灵
void SetImgSprite(int index, Image img)
{
spritIndex += index;
//超出精灵列表最大值等于第一个
if (spritIndex > spriteList.Count - 1)
spritIndex = 0;
//小于精灵列表最小值等于最后一个sprite
if (spritIndex < 0)
spritIndex = spriteList.Count - 1;
img.sprite = spriteList[spritIndex];
}
#endregion
}
?然后运行就可以了。
https://pan.baidu.com/s/1EdLVK-ZHgRI5RBX9JCqI6Q
提取码:syq1
|