3D动画实现游戏翻牌功能

2024-03-28 17:32

本文主要是介绍3D动画实现游戏翻牌功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:最近项目要做一个类似游戏翻取宝箱的功能来代替以前的签到打卡的功能,一开始完全没有思路,就连3D的翻转动画都不知道怎么实现,更别说还要结合一些特别的UI交互,更是无从下手;两天按我的思路实现之后,写到最后逻辑越来越复杂进行不下去,后来在小组组长的指点下,对牌进行了抽象简化了不少逻辑,进我实现最终完成了,^0^,先看下效果哈

1.积分类型(简单的积分上漂)
themeLove

2.入职红包(从当前位置移动到屏幕右下角我的模块)


3.实物类型(弹出对话框,分享后才能领取)


注:关于翻牌子的奖励规则:一般分为真随机和伪随机。1:真随机就是后台控制概率,完全随机,当然贵重物品的概率都是很低的或者直接是0,只是用来展示的,你永远抽不到的。。;2:伪随机就是不是完全的随机了,比如你的中奖概率更你的等级呀,你的充值金额呀相关联,比如把你的充值金额当做一个计算因数包含在计算你的中奖概率上,这样你充值消费的越多,中奖概率越大。当然这些概率之类的控制都是服务器端控制的,我们做Android前端只是负责调用接口,结合设计UI做展示而已,大头还是在服务器

下面是具体的实现过程步骤(为了用户体验:我们的这个抽奖页面是在用于调用抽奖接口成功之后才会出现的,其实就是只要这个抽奖的Activity能弹出,你的此次抽奖结果就已经确定了,剩下的就是我们客户端实现了,只要用户不管点击哪一个,我们就把你此次抽中的结果放到哪个位置,就是客户端根据调用接口返回来的数据来重新构造数据刷新Adapter)


(一)对不同类型牌进行抽象,把公用的属性和方法放在父类,让每种牌自身都具有执行不同动画的功能,提供不同的方法:(比如根据当前牌是否是抽奖结果决定是否延时翻转,根据当前牌的类型决定牌全部翻            转过来之后执行不同的动画)
(二)3D动画的实现
(三)整体布局是用GridView,设置用户未点击时GridView的默认显示效果
(四)然后是每个item的点击事件,根据抽奖结果,重新构造数据,刷新Adapter,同时根据抽中奖品的类型,来执行不同的UI交互


具体实现由于是公司项目,项目较大源代码不变给出,只是做部分截图

(一)抽象牌:

1.牌的类型:空牌(未点击时的默认牌);现金牌;蛙币牌;入职红包牌;实物牌;谢谢参与牌;目录结构如下
2.抽象牌的父类(Card):由于不同类型的牌要展示的效果不一样,而且点击过后翻转过来的View并不是一张简单的图片;比如说入职红包牌,其中的88元,88大礼包,还有背景图片都是不同的控件,因为这些字段都是从服务器获取我们用控件设置上去的,并不是只有一张图片的url让我们去加载,那样的话就简单很多了。这样的话,我们就不能只写一个type的布局了,应为那样的话,我们按正常的做法就是根据服务器给我们对象的类型来显示隐藏或显示不同的布局(可是这里我们有6中类型了呀,以后可能还要加),那样逻辑代码肯定会把你写晕的,而且bug百出,当然这也是对多类型数据对象展示最low的一种方法了;我们当然可以重新适配器中getTypeCount的方法在getView的时候更具不同的type来inflater不同的layout文件,这样做法显然比只用一中type要好,(ps:楼主开始也是这么做的,可是写到中间发现也是比较复杂),但是这样写不便于扩展,后面要加牌的类型我们还是要得改adapter里面getTypeCount和getView里面的代码,不便于扩展,而且每种牌要根据类型 要执行不同的后续操作,考虑再三我们还是得将其抽象成一个Card父类,并在父类中定义一些共有的方法,比如说getView方法返回翻转过来要展示的视图。

代码如下:
public abstract class Card {Context mContext;LotteryItem mItem;View mView;boolean isHit;//是否是选中的那个View mFlopView;VLImageView mFlopView1;View mFlopView2;int mIndex = 0;int mRotateX;int mRotateY;public Card(Context context) {mContext = context;
//        getView();}public Card(Context context, LotteryItem item) {mContext = context;mItem = item;
//        getView();}public void setHit(boolean b) {isHit = b;}public abstract View getView();public abstract void prepareAnimation();public abstract void startAnimation();public abstract void endAnimation();public abstract void endAnimation(int left, int itemWidth, int top, int itemHeight);
/*    public View getViewDiaplay(){if (mView!=null){return mView;}else{return null;}}*/public abstract void setRotateXY(int x, int y);
}
3.具体每种类型牌的具体类型(这里只拿其中2种来说明,一种默认图(未翻转类型),一种较为复杂的入职红包类型)。
EmptyCard:(这里只要实现getView方法即可,其余可以不实现,因为这里只是EmptyCard只是未点击时的默认展示牌,且图片也是本地的)
public class EmptyCard extends  Card {public EmptyCard(Context context) {super(context);}@Overridepublic void endAnimation() {<span style="color:#ff6666;"> </span>}@Overridepublic View getView() {<span style="white-space:pre">	</span><span style="color:#ff0000;">//空牌是一张默认图片,直接加载本地图片,这里是用fresco封装的加载默认图片方法</span>I90ImageLoaderModel mI90ImageLoaderModel = VLApplication.instance().getModel(I90ImageLoaderModel.class);View view = View.inflate(mContext, R.layout.item_empty_card, null);VLImageView cardPic = (VLImageView) view.findViewById(R.id.emptyCardPic);int rail= VLUtils.dip2px(4);cardPic.setCornersRadii(rail,rail,rail,rail);cardPic.setVlScaleType(VLImageView.VLSCALE_TYPE_CENTER_CROP);cardPic.apply();mI90ImageLoaderModel.renderDrawableImage(R.drawable.card_default,200,200,cardPic);mView=view;return view;}@Overridepublic void prepareAnimation() {if (isHit){startAnimation();}else{  <span style="color:#ff0000;"> // VLScheduler是项目中封装的类似Handler类</span>VLScheduler.instance.schedule(1000, VLScheduler.THREAD_MAIN, new VLBlock() {@Overrideprotected void process(boolean canceled) {startAnimation();}});}}@Overridepublic void startAnimation() {<span style="color:#ff0000;">//结合Rotate3dAnimation实现3D翻转效果</span>Rotate3dAnimation rotation = new Rotate3dAnimation(0, 90, mRotateX, mRotateY, 0.0f, true);rotation.setDuration(500);rotation.setFillAfter(true);rotation.setInterpolator(new AccelerateInterpolator());
//      rotation.setAnimationListener(new);rotation.setAnimationListener(new Animation.AnimationListener(){@Overridepublic void onAnimationEnd(Animation animation) {mView.post(new SwapViews());}@Overridepublic void onAnimationRepeat(Animation animation) {}@Overridepublic void onAnimationStart(Animation animation) {}});mFlopView.startAnimation(rotation);}private class SwapViews implements Runnable{@Overridepublic void run() {mFlopView1.setVisibility(View.GONE);mFlopView2.setVisibility(View.GONE);mIndex++;if (0 == mIndex % 2) {mFlopView = mFlopView1;} else {mFlopView = mFlopView2;}mFlopView.setVisibility(View.VISIBLE);mFlopView.requestFocus();Rotate3dAnimation rotation = new Rotate3dAnimation(-90, 0, mRotateX, mRotateY, 0.0f, false);rotation.setDuration(500);rotation.setFillAfter(true);rotation.setInterpolator(new DecelerateInterpolator());mFlopView.startAnimation(rotation);}}public void endAnimation(int left,int itemWidth,int top,int itemHeight) {AnimationSet floatAnimSet = new AnimationSet(true);TranslateAnimation transAnim = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0, Animation.ABSOLUTE, VLUtils.getScreenWidth(mContext)-left-itemWidth,Animation.RELATIVE_TO_SELF,0, Animation.ABSOLUTE, VLUtils.getScreenHeight(mContext)-top-itemHeight);transAnim.setDuration(2000);ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);scaleAnim.setDuration(2000);
//        scaleAnim.setFillAfter(true);AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);alphaAnim.setDuration(2000);floatAnimSet.addAnimation(scaleAnim);floatAnimSet.addAnimation(transAnim);floatAnimSet.addAnimation(alphaAnim);mView.startAnimation(floatAnimSet);}@Overridepublic void setRotateXY(int x, int y) {mRotateX=x;mRotateY=y;}
}

JobCard
public class JobCard extends Card {public JobCard(Context context,LotteryItem item) {<span style="color:#ff0000;">//LotteryItem是服务器获取的奖品对象</span>super(context,item);}public void endAnimation(int left,int itemWidth,int top,int itemHeight) {<span style="color:#ff0000;">//入职红包牌对应的是要从所点击的地方执行动画到右下角,其中的参数是从外部调用的地方经过动态测量后传递进来的,另外特别的是在gridView直接执行子View的移动,子View是不会超出GridView这个大的父布局的,而且是从gridView的底部移动的,(分割线的底部,要有多丑就有多丑。。。),所以我们要根据点击的位置重新创建一个牌对象放在整个Activity的根布局上并且它的位置要和我们点击的位置要完全重合,这样才不会漏出破绽;</span>AnimationSet floatAnimSet = new AnimationSet(true);TranslateAnimation transAnim = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0, Animation.ABSOLUTE, VLUtils.getScreenWidth(mContext)-left-itemWidth,Animation.RELATIVE_TO_SELF,0, Animation.ABSOLUTE, VLUtils.getScreenHeight(mContext)-top-itemHeight);transAnim.setDuration(2000);ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);scaleAnim.setDuration(2000);
//        scaleAnim.setFillAfter(true);AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);alphaAnim.setDuration(2000);floatAnimSet.addAnimation(scaleAnim);floatAnimSet.addAnimation(transAnim);floatAnimSet.addAnimation(alphaAnim);floatAnimSet.setFillAfter(true);mView.startAnimation(floatAnimSet);}@Overridepublic View getView() {<span style="color:#ff0000;">//根据传递进来的LotteryItem属性,初始化View视图</span>I90ImageLoaderModel mI90ImageLoaderModel = VLApplication.instance().getModel(I90ImageLoaderModel.class);int rail = VLUtils.dip2px(4);View view = View.inflate(mContext, R.layout.item_job_card, null);VLImageView cardEmpty = (VLImageView) view.findViewById(R.id.jobEmpty);cardEmpty.setCornersRadii(rail,rail,rail,rail);cardEmpty.setVlScaleType(VLImageView.VLSCALE_TYPE_CENTER_CROP);cardEmpty.apply();mI90ImageLoaderModel.renderDrawableImage(R.drawable.card_default,200,200,cardEmpty);View cardView = view.findViewById(R.id.jobView);VLImageView cardTypeImage = (VLImageView) view.findViewById(R.id.jobCardTypeImage);TextView cardCount =(TextView) view.findViewById(R.id.jobCardCount);TextView cardType = (TextView)view.findViewById(R.id.jobCardType);ImageView cardBg = (ImageView)view.findViewById(R.id.jobCardBg);cardTypeImage.setCornersRadii(0, 0, rail, rail);cardTypeImage.setVlScaleType(VLImageView.VLSCALE_TYPE_CENTER_CROP);cardTypeImage.apply();mI90ImageLoaderModel.renderDrawableImage(R.drawable.card_image1, 200, 52, cardTypeImage);cardCount.setText(VLUtils.androidSizeSpan((int)mItem.getPrototype().getCash()+"", 0xfff55c3d, 0, 60));cardCount.append(VLUtils.androidSizeSpan("元", 0xfff55c3d, 0, 24));cardBg.setVisibility(isHit ? View.GONE : View.VISIBLE);if (TextUtils.isEmpty(mItem.getPrototype().getName())){cardType.setText("入职红包");}else{cardType.setText(mItem.getPrototype().getName());}mFlopView=cardEmpty;mFlopView1=cardEmpty;mFlopView2=cardView;mView=view;return view;}@Overridepublic void prepareAnimation() {VLDebug.logD("startAnimation=" + " class=" + getClass().getSimpleName() + "isHit="+isHit);if (isHit){<span style="color:#ff0000;">//根据当前牌是否是用户本次抽中的牌,来决定是否来延迟翻转,从gif图中可以看出,用户点中的牌立刻执行翻转动画,而其他的牌有1秒钟的延迟</span>startAnimation();}else{<span style="white-space:pre">	</span>  <span style="color:#ff0000;"> //延迟翻转</span>VLScheduler.instance.schedule(1000, VLScheduler.THREAD_MAIN, new VLBlock() {@Overrideprotected void process(boolean canceled) {startAnimation();}});}}@Overridepublic void startAnimation() {<span style="color:#ff0000;">//真正翻转动画</span>VLDebug.logD("startAnimation= do" + " class=" + getClass().getSimpleName() + "isHit="+isHit);Rotate3dAnimation rotation = new Rotate3dAnimation(0, 90, mRotateX, mRotateY, 0.0f, true);rotation.setDuration(500);rotation.setFillAfter(true);rotation.setInterpolator(new AccelerateInterpolator());
//      rotation.setAnimationListener(new);rotation.setAnimationListener(new Animation.AnimationListener(){@Overridepublic void onAnimationEnd(Animation animation) {mView.post(new SwapViews());}@Overridepublic void onAnimationRepeat(Animation animation) {}@Overridepublic void onAnimationStart(Animation animation) {}});mFlopView.startAnimation(rotation);}@Overridepublic void endAnimation() {}
<span style="color:#ff0000;">//  Rotate3dAnimation结合该类实现3d翻转,原理是2个View同时执行翻转动画,到一定角度显示隐藏一个布局</span>private class SwapViews implements Runnable{@Overridepublic void run() {mFlopView1.setVisibility(View.GONE);mFlopView2.setVisibility(View.GONE);mIndex++;if (0 == mIndex % 2) {mFlopView = mFlopView1;} else {mFlopView = mFlopView2;}mFlopView.setVisibility(View.VISIBLE);Rotate3dAnimation rotation = new Rotate3dAnimation(-90, 0, mRotateX, mRotateY, 0.0f, false);rotation.setDuration(500);rotation.setFillAfter(true);rotation.setInterpolator(new DecelerateInterpolator());mFlopView.startAnimation(rotation);}}@Overridepublic void setRotateXY(int x, int y) {<span style="color:#ff0000;">//设置旋转的轴线(属性动画的同学应该都知道执行动画应该按照一定的轴线旋转)这里的值也是外部调用时动态测量每个GridView的Item的宽高后设置进来的</span>mRotateX=x;mRotateY=y;}
}

(二 )3D动画的执行

3D翻转的动画Android本身并不支持,可以利用valueAnimator和objectAnimator中rotationY,rotationX,rotationZ可以实现由3d的效果,但是只支持一张图片,所以用的时候还是要封装,楼主当时做的时候对valueAnimator和objectAnimator还不是很了解,所以还是从网上搜罗了一个自定义动画Rotate3dAnimation,这个应该是一个老外写的ps:注释全是英文。。。后来楼主看了这位大神的blog: http://my.csdn.net/harvic880925的博客后自己实现了一个支持3d动画的自定义控件( http://blog.csdn.net/themelove/article/details/50619771)效果还不错

(三)每个item的点击事件(根据抽奖结果,重新构造数据,刷新Adapter,同时根据抽中奖品的类型,来执行不同的UI交互,当然是在Activity和Adapter里进行了)


MyFlopAdaper
public class MyFlopAdapter extends BaseAdapter {private LayoutInflater mInflater;private ArrayList<Card> mDatas;private int mHeight;VLAsyncHandler mVlAsyncHandler;public void setLoadEnd(VLAsyncHandler vlAsyncHandler) {mVlAsyncHandler = vlAsyncHandler;}public MyFlopAdapter(Context context, int height) {mInflater = LayoutInflater.from(context);mHeight = height;}public void setData(ArrayList<Card> datas) {mDatas = datas;}@Overridepublic int getCount() {return mDatas.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (convertView == null) {convertView = mInflater.inflate(R.layout.group_daydayflop_row_item, parent, false);convertView.setLayoutParams(new AbsListView.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, mHeight / 3- VLUtils.dip2px(8)));}if (position== parent.getChildCount()) {FrameLayout frameLayout = (FrameLayout) convertView;frameLayout.removeAllViews();frameLayout.addView(mDatas.get(position).getView(), new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));if (null != mVlAsyncHandler && position == mDatas.size() - 1) {mVlAsyncHandler.handlerSuccess();}}VLDebug.logD("getView  position= " + position);return convertView;}}

DaydayFlopActivity(代码太多,直接上这几个相关类的压缩包吧)

相关类压缩包 http://download.csdn.net/detail/themelove/9424747




这篇关于3D动画实现游戏翻牌功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/856251

相关文章

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

JAVA利用顺序表实现“杨辉三角”的思路及代码示例

《JAVA利用顺序表实现“杨辉三角”的思路及代码示例》杨辉三角形是中国古代数学的杰出研究成果之一,是我国北宋数学家贾宪于1050年首先发现并使用的,:本文主要介绍JAVA利用顺序表实现杨辉三角的思... 目录一:“杨辉三角”题目链接二:题解代码:三:题解思路:总结一:“杨辉三角”题目链接题目链接:点击这里

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主