Unity实现游戏卡牌滚动效果
最近项目中的活动面板要做来回滚动卡牌预览效果,感觉自己来写的话,也能写,但是可能会比较耗时,看到github上有开源的项目,于是就借用了,github的资源地址,感谢作者的分享。
本篇博客旨在告诉大家如何利用这个插件。
插件的核心在于工程中的6个脚本,以下是六个脚本的源码:
dragenhanceview.cs
using unityengine; using system.collections; using unityengine.ui; using unityengine.eventsystems; public class uguienhanceitem : enhanceitem { private button ubutton; private image image; protected override void onstart() { image = getcomponent<image>(); ubutton = getcomponent<button>(); ubutton.onclick.addlistener(onclickuguibutton); } private void onclickuguibutton() { onclickenhanceitem(); } // set the item "depth" 2d or 3d protected override void setitemdepth(float depthcurvevalue, int depthfactor, float itemcount) { int newdepth = (int)(depthcurvevalue * itemcount); this.transform.setsiblingindex(newdepth); } public override void setselectstate(bool iscenter) { if (image == null) image = getcomponent<image>(); image.color = iscenter ? color.white : color.gray; } }
enhancescrollviewdragcontroller.cs
using unityengine; using system.collections; public class enhancescrollviewdragcontroller : monobehaviour { private vector2 lastposition = vector2.zero; private vector2 cachedposition = vector2.zero; private gameobject dragtarget; private camera targetcamera; private int raycastmask = 0; private bool dragstart = false; public void settargetcameraandmask(camera camera, int mask) { this.targetcamera = camera; this.raycastmask = mask; } void update() { if (this.targetcamera == null) return; #if unity_editor processmouseinput(); #elif unity_ios || unity_android processtouchinput(); #endif } /// <summary> /// process mouse input /// </summary> private void processmouseinput() { if (input.getmousebuttondown(0)) { if (targetcamera == null) return; dragtarget = raycast(this.targetcamera, input.mouseposition); lastposition.x = input.mouseposition.x; lastposition.y = input.mouseposition.y; } if (input.getmousebutton(0)) { if (dragtarget == null) return; cachedposition.x = input.mouseposition.x; cachedposition.y = input.mouseposition.y; vector2 delta = cachedposition - lastposition; if (!dragstart && delta.sqrmagnitude != 0f) dragstart = true; if (dragstart) { // notify target dragtarget.sendmessage("onenhanceviewdrag", delta, sendmessageoptions.dontrequirereceiver); } lastposition = cachedposition; } if (input.getmousebuttonup(0)) { if (dragtarget != null && dragstart) { dragtarget.sendmessage("onenhaneviewdragend", sendmessageoptions.dontrequirereceiver); } dragtarget = null; dragstart = false; } } /// <summary> /// process touch input /// </summary> private void processtouchinput() { if (input.touchcount > 0) { touch touch = input.gettouch(0); if (touch.phase == touchphase.began) { if (targetcamera == null) return; dragtarget = raycast(this.targetcamera, input.mouseposition); } else if (touch.phase == touchphase.moved) { if (dragtarget == null) return; if (!dragstart && touch.deltaposition.sqrmagnitude != 0f) { dragstart = true; } if (dragstart) { // notify target dragtarget.sendmessage("onenhanceviewdrag", touch.deltaposition, sendmessageoptions.dontrequirereceiver); } } else if (touch.phase == touchphase.ended) { if (dragtarget != null && dragstart) { dragtarget.sendmessage("onenhaneviewdragend", sendmessageoptions.dontrequirereceiver); } dragtarget = null; dragstart = false; } } } public gameobject raycast(camera cam, vector3 inpos) { vector3 pos = cam.screentoviewportpoint(inpos); if (float.isnan(pos.x) || float.isnan(pos.y)) return null; if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f) return null; ray ray = cam.screenpointtoray(inpos); float dis = 100f; raycasthit[] hits = physics.raycastall(ray, dis, raycastmask); if (hits.length > 0) { for (int i = 0; i < hits.length; i++) { gameobject go = hits[i].collider.gameobject; dragenhanceview dragview = go.getcomponent<dragenhanceview>(); if (dragview == null) continue; else { // just return current hover object our drag target return go; } } } return null; } }
enhanceitem.cs
using unityengine; using system.collections; public class enhanceitem : monobehaviour { // start index private int curveoffsetindex = 0; public int curveoffsetindex { get { return this.curveoffsetindex; } set { this.curveoffsetindex = value; } } // runtime real index(be calculated in runtime) private int currealindex = 0; public int realindex { get { return this.currealindex; } set { this.currealindex = value; } } // curve center offset private float dcurvecenteroffset = 0.0f; public float centeroffset { get { return this.dcurvecenteroffset; } set { dcurvecenteroffset = value; } } private transform mtrs; void awake() { mtrs = this.transform; onawake(); } void start() { onstart(); } // update item's status // 1. position // 2. scale // 3. "depth" is 2d or z position in 3d to set the front and back item public void updatescrollviewitems( float xvalue, float depthcurvevalue, int depthfactor, float itemcount, float yvalue, float scalevalue) { vector3 targetpos = vector3.one; vector3 targetscale = vector3.one; // position targetpos.x = xvalue; targetpos.y = yvalue; mtrs.localposition = targetpos; // set the "depth" of item // targetpos.z = depthvalue; setitemdepth(depthcurvevalue, depthfactor, itemcount); // scale targetscale.x = targetscale.y = scalevalue; mtrs.localscale = targetscale; } protected virtual void onclickenhanceitem() { enhancescrollview.getinstance.sethorizontaltargetitemindex(this); } protected virtual void onstart() { } protected virtual void onawake() { } protected virtual void setitemdepth(float depthcurvevalue, int depthfactor, float itemcount) { } // set the item center state public virtual void setselectstate(bool iscenter) { } }
enhancescrollview.cs
using unityengine; using system.collections; using system.collections.generic; public class enhancescrollview : monobehaviour { public enum inputsystemtype { nguiandworldinput, // use enhancescrollviewdragcontroller.cs to get the input(keyboard and touch) uguiinput, // use udragenhanceview for each item to get drag event } // input system type(ngui or 3d world, ugui) public inputsystemtype inputtype = inputsystemtype.nguiandworldinput; // control the item's scale curve public animationcurve scalecurve; // control the position curve public animationcurve positioncurve; // control the "depth"'s curve(in 3d version just the z value, in 2d ui you can use the depth(ngui)) // note: // 1. in ngui set the widget's depth may cause performance problem // 2. if you use 3d ui just set the item's z position public animationcurve depthcurve = new animationcurve(new keyframe(0, 0), new keyframe(0.5f, 1), new keyframe(1, 0)); // the start center index [tooltip("the start center index")] public int startcenterindex = 0; // offset width between item public float cellwidth = 10f; private float totalhorizontalwidth = 500.0f; // vertical fixed position value public float yfixedpositionvalue = 46.0f; // lerp duration public float lerpduration = 0.2f; private float mcurrentduration = 0.0f; private int mcenterindex = 0; public bool enablelerptween = true; // center and precentered item private enhanceitem curcenteritem; private enhanceitem precenteritem; // if we can change the target item private bool canchangeitem = true; private float dfactor = 0.2f; // originhorizontalvalue lerp to horizontaltargetvalue private float originhorizontalvalue = 0.1f; public float curhorizontalvalue = 0.5f; // "depth" factor (2d widget depth or 3d z value) private int depthfactor = 5; // drag enhance scroll view [tooltip("camera for drag ray cast")] public camera sourcecamera; private enhancescrollviewdragcontroller dragcontroller; public void enabledrag(bool isenabled) { if (isenabled) { if (inputtype == inputsystemtype.nguiandworldinput) { if (sourcecamera == null) { debug.logerror("## source camera for drag scroll view is null ##"); return; } if (dragcontroller == null) dragcontroller = gameobject.addcomponent<enhancescrollviewdragcontroller>(); dragcontroller.enabled = true; // set the camera and mask dragcontroller.settargetcameraandmask(sourcecamera, (1 << layermask.nametolayer("ui"))); } } else { if (dragcontroller != null) dragcontroller.enabled = false; } } // targets enhance item in scroll view public list<enhanceitem> listenhanceitems; // sort to get right index private list<enhanceitem> listsorteditems = new list<enhanceitem>(); private static enhancescrollview instance; public static enhancescrollview getinstance { get { return instance; } } void awake() { instance = this; } void start() { canchangeitem = true; int count = listenhanceitems.count; dfactor = (mathf.roundtoint((1f / count) * 10000f)) * 0.0001f; mcenterindex = count / 2; if (count % 2 == 0) mcenterindex = count / 2 - 1; int index = 0; for (int i = count - 1; i >= 0; i--) { listenhanceitems[i].curveoffsetindex = i; listenhanceitems[i].centeroffset = dfactor * (mcenterindex - index); listenhanceitems[i].setselectstate(false); gameobject obj = listenhanceitems[i].gameobject; if (inputtype == inputsystemtype.nguiandworldinput) { dragenhanceview script = obj.getcomponent<dragenhanceview>(); if (script != null) script.setscrollview(this); } else { udragenhanceview script = obj.getcomponent<udragenhanceview>(); if (script != null) script.setscrollview(this); } index++; } // set the center item with startcenterindex if (startcenterindex < 0 || startcenterindex >= count) { debug.logerror("## startcenterindex < 0 || startcenterindex >= listenhanceitems.count out of index ##"); startcenterindex = mcenterindex; } // sorted items listsorteditems = new list<enhanceitem>(listenhanceitems.toarray()); totalhorizontalwidth = cellwidth * count; curcenteritem = listenhanceitems[startcenterindex]; curhorizontalvalue = 0.5f - curcenteritem.centeroffset; lerptweentotarget(0f, curhorizontalvalue, false); // // enable the drag actions // enabledrag(true); } private void lerptweentotarget(float originvalue, float targetvalue, bool needtween = false) { if (!needtween) { sortenhanceitem(); originhorizontalvalue = targetvalue; updateenhancescrollview(targetvalue); this.ontweenover(); } else { originhorizontalvalue = originvalue; curhorizontalvalue = targetvalue; mcurrentduration = 0.0f; } enablelerptween = needtween; } public void disablelerptween() { this.enablelerptween = false; } /// /// update enhanceitem state with curve ftime value /// public void updateenhancescrollview(float fvalue) { for (int i = 0; i < listenhanceitems.count; i++) { enhanceitem itemscript = listenhanceitems[i]; float xvalue = getxposvalue(fvalue, itemscript.centeroffset); float scalevalue = getscalevalue(fvalue, itemscript.centeroffset); float depthcurvevalue = depthcurve.evaluate(fvalue + itemscript.centeroffset); itemscript.updatescrollviewitems(xvalue, depthcurvevalue, depthfactor, listenhanceitems.count, yfixedpositionvalue, scalevalue); } } void update() { if (enablelerptween) tweenviewtotarget(); } private void tweenviewtotarget() { mcurrentduration += time.deltatime; if (mcurrentduration > lerpduration) mcurrentduration = lerpduration; float percent = mcurrentduration / lerpduration; float value = mathf.lerp(originhorizontalvalue, curhorizontalvalue, percent); updateenhancescrollview(value); if (mcurrentduration >= lerpduration) { canchangeitem = true; enablelerptween = false; ontweenover(); } } private void ontweenover() { if (precenteritem != null) precenteritem.setselectstate(false); if (curcenteritem != null) curcenteritem.setselectstate(true); } // get the evaluate value to set item's scale private float getscalevalue(float slidervalue, float added) { float scalevalue = scalecurve.evaluate(slidervalue + added); return scalevalue; } // get the x value set the item's position private float getxposvalue(float slidervalue, float added) { float evaluatevalue = positioncurve.evaluate(slidervalue + added) * totalhorizontalwidth; return evaluatevalue; } private int getmovecurvefactorcount(enhanceitem precenteritem, enhanceitem newcenteritem) { sortenhanceitem(); int factorcount = mathf.abs(newcenteritem.realindex) - mathf.abs(precenteritem.realindex); return mathf.abs(factorcount); } // sort item with x so we can know how much distance we need to move the timeline(curve time line) static public int sortposition(enhanceitem a, enhanceitem b) { return a.transform.localposition.x.compareto(b.transform.localposition.x); } private void sortenhanceitem() { listsorteditems.sort(sortposition); for (int i = listsorteditems.count - 1; i >= 0; i--) listsorteditems[i].realindex = i; } public void sethorizontaltargetitemindex(enhanceitem selectitem) { if (!canchangeitem) return; if (curcenteritem == selectitem) return; canchangeitem = false; precenteritem = curcenteritem; curcenteritem = selectitem; // calculate the direction of moving float centerxvalue = positioncurve.evaluate(0.5f) * totalhorizontalwidth; bool isright = false; if (selectitem.transform.localposition.x > centerxvalue) isright = true; // calculate the offset * dfactor int moveindexcount = getmovecurvefactorcount(precenteritem, selectitem); float dvalue = 0.0f; if (isright) { dvalue = -dfactor * moveindexcount; } else { dvalue = dfactor * moveindexcount; } float originvalue = curhorizontalvalue; lerptweentotarget(originvalue, curhorizontalvalue + dvalue, true); } // click the right button to select the next item. public void onbtnrightclick() { if (!canchangeitem) return; int targetindex = curcenteritem.curveoffsetindex + 1; if (targetindex > listenhanceitems.count - 1) targetindex = 0; sethorizontaltargetitemindex(listenhanceitems[targetindex]); } // click the left button the select next next item. public void onbtnleftclick() { if (!canchangeitem) return; int targetindex = curcenteritem.curveoffsetindex - 1; if (targetindex < 0) targetindex = listenhanceitems.count - 1; sethorizontaltargetitemindex(listenhanceitems[targetindex]); } public float factor = 0.001f; // on drag move public void ondragenhanceviewmove(vector2 delta) { // in developing if (mathf.abs(delta.x) > 0.0f) { curhorizontalvalue += delta.x * factor; lerptweentotarget(0.0f, curhorizontalvalue, false); } } // on drag end public void ondragenhanceviewend() { // find closed item to be centered int closestindex = 0; float value = (curhorizontalvalue - (int)curhorizontalvalue); float min = float.maxvalue; float tmp = 0.5f * (curhorizontalvalue < 0 ? -1 : 1); for (int i = 0; i < listenhanceitems.count; i++) { float dis = mathf.abs(mathf.abs(value) - mathf.abs((tmp - listenhanceitems[i].centeroffset))); if (dis < min) { closestindex = i; min = dis; } } originhorizontalvalue = curhorizontalvalue; float target = ((int)curhorizontalvalue + (tmp - listenhanceitems[closestindex].centeroffset)); precenteritem = curcenteritem; curcenteritem = listenhanceitems[closestindex]; lerptweentotarget(originhorizontalvalue, target, true); canchangeitem = false; } }
nguienhanceitem.cs
using unityengine; using system.collections; /// <summary> /// ngui enhance item example /// </summary> public class nguienhanceitem : enhanceitem { private uitexture mtexture; protected override void onawake() { this.mtexture = getcomponent<uitexture>(); uieventlistener.get(this.gameobject).onclick = onclicknguiitem; } private void onclicknguiitem(gameobject obj) { this.onclickenhanceitem(); } // set the item "depth" 2d or 3d protected override void setitemdepth(float depthcurvevalue, int depthfactor, float itemcount) { if (mtexture.depth != (int)mathf.abs(depthcurvevalue * depthfactor)) mtexture.depth = (int)mathf.abs(depthcurvevalue * depthfactor); } // item is centered public override void setselectstate(bool iscenter) { if (mtexture == null) mtexture = this.getcomponent<uitexture>(); if (mtexture != null) mtexture.color = iscenter ? color.white : color.gray; } protected override void onclickenhanceitem() { // item was clicked base.onclickenhanceitem(); } }
uguienhanceitem.cs
using unityengine; using system.collections; using unityengine.ui; using unityengine.eventsystems; public class uguienhanceitem : enhanceitem { private button ubutton; private image image; protected override void onstart() { image = getcomponent<image>(); ubutton = getcomponent<button>(); ubutton.onclick.addlistener(onclickuguibutton); } private void onclickuguibutton() { onclickenhanceitem(); } // set the item "depth" 2d or 3d protected override void setitemdepth(float depthcurvevalue, int depthfactor, float itemcount) { int newdepth = (int)(depthcurvevalue * itemcount); this.transform.setsiblingindex(newdepth); } public override void setselectstate(bool iscenter) { if (image == null) image = getcomponent<image>(); image.color = iscenter ? color.white : color.gray; } }
导入以上6个脚本以后,我们开始制作效果,先从ngui开始,我们先在场景中,随便添加一个背景,然后,我们在uiroot下面添加一个空物体,取名scrollview,添加enhancescrollview.cs脚本,然后制作六个texture作为scrollview的子物体,添加dragenhanceview.cs脚本,nguienhanceitem.cs脚本,boxcollider组件。接着,我们在六个图片下方添加两个button,作为左右切换卡牌的按钮,在点击事件中,拖入scrollview,分别添加onbtnleftclick,onbtnrightclick方法。做完以上操作以后,场景大概是这样:
接着,我们选中scrollview,调整脚本参数:
scalecurve图像参数,设置为如下,左右循环都为pingpong:
positioncurve图像参数如下,左右循环都为loop:
depthcurve图像参数如下,左右循环都为loop:
然后把scrollview的子物体都拖到listenhanceitems这个公开数组下:
这样,我们就把配置工作都做好了,运行游戏:
可以看到,效果还不错,左右滑动或者点击切换按钮,就能实现切换卡牌的功能。
接着,我们看一下ugui的实现,ugui的ui布局基本和ngui保持一致,所不同的是scrollview的子物体添加的脚本不一样,所需要的脚本及组件如下图所示:
然后,还有需要注意的一点是,在scrollview上的参数配置上,我们需要把inputtype这个属性调整为ugui input
曲线设置和子物体数组设置和ngui一样,这里就不再重复了,配置完这些操作以后,运行,ugui也能实现一样的卡牌滚动效果:
以上,感谢github。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: HTML5新增的Css选择器、伪类介绍