首页 文章 精选 留言 我的

精选列表

搜索[android studio],共10000篇文章
优秀的个人博客,低调大师

android java.io.IOException: open failed: EBUSY (Device or resource bu...

今天遇到一个奇怪的问题, 测试在程序的下载界面,下载一个文件第一次下载成功,删除后再下载结果下载报错, 程序:file.createNewFile(); 报错:java.io.IOException: open failed: EBUSY (Device or resource busy) 程序:RandomAccessFile randomAccessFile = new RandomAccessFile(localfile, "rwd"); 报错:java.io.FileNotFoundException: open failed: EBUSY (Device or resource busy) at libcore.io.IoBridge.open ......一啪啦 这些手机都没问题,就只有华为的一款有这个问题,在网上查了,说的是FAT32 system, 这是这个测试机的系统,具体的不知道跟这个到底有没关系,反正根据网上一段话说的是把要删除的文件改名然后再删除,下次下载时,就算没删掉,文件名也改了,所以第二次就能下载了。 删除文件代码: public void deleteSDCardFolder(File dir) { File to = new File(dir.getAbsolutePath() + System.currentTimeMillis()); dir.renameTo(to); if (to.isDirectory()) { String[] children = to.list(); for (int i = 0; i < children.length; i++) { File temp = new File(to, children[i]); if (temp.isDirectory()) { deleteSDCardFolder(temp); } else { boolean b = temp.delete(); if (b == false) { Log.d("deleteSDCardFolder", "DELETE FAIL"); } } } to.delete(); } 解决问题参考网址:http://stackoverflow.com/questions/11539657/open-failed-ebusy-device-or-resource-busy 本文转自 一点点征服 博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/5581517.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(18)

Step 16.InputDispatcher.prepareDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::prepareDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection,EventEntry*eventEntry,constInputTarget*inputTarget, boolresumeWithAppendedMotionSample){ ...... //Resumethedispatchcyclewithafreshlyappendedmotionsample. //Firstwecheckthatthelastdispatchentryintheoutboundqueueisforthesame //motioneventtowhichweappendedthemotionsample.Ifwefindsuchadispatch //entry,andifitiscurrentlyinprogressthenwetrytostreamthenewsample. boolwasEmpty=connection->outboundQueue.isEmpty(); if(!wasEmpty&&resumeWithAppendedMotionSample){ ...... return; } //Thisisanewevent. //Enqueueanewdispatchentryontotheoutboundqueueforthisconnection. DispatchEntry*dispatchEntry=mAllocator.obtainDispatchEntry(eventEntry,//incrementsref inputTarget->flags,inputTarget->xOffset,inputTarget->yOffset); ...... //Enqueuethedispatchentry. connection->outboundQueue.enqueueAtTail(dispatchEntry); //Iftheoutboundqueuewaspreviouslyempty,startthedispatchcyclegoing. if(wasEmpty){ ...... startDispatchCycleLocked(currentTime,connection); } } 在开始处理键盘事件之前,这个函数会检查一下传进来的参数connection中的outboundQueue事件队列是否为空,如果不为空,就要看看当前要处理的事件和outboundQueue队列中的最后一个事件是不是同一个motion事件,如果是的话,并且从上面传进来的resumeWithAppendedMotionSample参数为true,这时候就要以流水线的方式来处理这些motion事件了。在我们这个情景中,要处理的是键盘事件,因此在上面Step 12中传进来的resumeWithAppendedMotionSample参数为false,因此,我们略过这种情况。 接下来,就会把当前的键盘事件封装成一个DispatchEntry对象,然后添加到connection对象的outboundQueue队列中去,表示当前键盘事件是一个待处理的键盘事件。 当connection中的outboundQueue事件队列不为空,即wasEmpty为false时,说明当前这个Activity窗口正在处键盘事件了,因此,就不需要调用startDispatchCycleLocked来启动Activity窗口来处理这个事件了,因为一旦这个Activity窗口正在处键盘事件,它就会一直处理下去,直到它里的connection对象的outboundQueue为空为止。当connection中的outboundQueue事件队列为空时,就需要调用startDispatchCycleLocked来通知这个Activity窗口来执行键盘事件处理的流程了。 Step 17.InputDispatcher.startDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::startDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection){ ...... DispatchEntry*dispatchEntry=connection->outboundQueue.headSentinel.next; //Markthedispatchentryasinprogress. dispatchEntry->inProgress=true; //Updatetheconnection'sinputstate. EventEntry*eventEntry=dispatchEntry->eventEntry; ...... //Publishtheevent. status_tstatus; switch(eventEntry->type){ caseEventEntry::TYPE_KEY:{ KeyEntry*keyEntry=static_cast<KeyEntry*>(eventEntry); //Applytargetflags. int32_taction=keyEntry->action; int32_tflags=keyEntry->flags; //Publishthekeyevent. status=connection->inputPublisher.publishKeyEvent(keyEntry->deviceId,keyEntry->source, action,flags,keyEntry->keyCode,keyEntry->scanCode, keyEntry->metaState,keyEntry->repeatCount,keyEntry->downTime, keyEntry->eventTime); ...... break; } ...... } //Sendthedispatchsignal. status=connection->inputPublisher.sendDispatchSignal(); ...... } 这个函数主要围绕传进来的Connection对象做两件事情,一是从它的outboundQueue队列中取出当前需要处理的键盘事件,然后把这个事件记录在它的内部对象inputPublisher中,二是通过它的内部对象inputPublisher通知它所关联的Activity窗口,现在有键盘事件需要处理了。第一件事情是通过调用它的InputPublisher对象的publishKeyEvent函数来完成的,而第二件事情是通过调用它的InputPublisher对象的sendDispatchSignal来完成的。我们先来看InputPublisher的成员函数publishKeyEvent的实现,然后再回来分析它的另外一个成员函数sendDispatchSignal的实现。 Step 18. InputPublisher.publishKeyEvent 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::publishKeyEvent( int32_tdeviceId, int32_tsource, int32_taction, int32_tflags, int32_tkeyCode, int32_tscanCode, int32_tmetaState, int32_trepeatCount, nsecs_tdownTime, nsecs_teventTime){ ...... status_tresult=publishInputEvent(AINPUT_EVENT_TYPE_KEY,deviceId,source); if(result<0){ returnresult; } mSharedMessage->key.action=action; mSharedMessage->key.flags=flags; mSharedMessage->key.keyCode=keyCode; mSharedMessage->key.scanCode=scanCode; mSharedMessage->key.metaState=metaState; mSharedMessage->key.repeatCount=repeatCount; mSharedMessage->key.downTime=downTime; mSharedMessage->key.eventTime=eventTime; returnOK; } 这个函数主要就是把键盘事件记录在InputPublisher类的成员变量mSharedMessage中了,这个mSharedMessage成员变量指向的是一个匿名共享内存。 这个匿名共享内存是什么时候创建的呢?前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封装成一个 Connection对象时,会调用它的initialize成员函数来执行一些初始化工作,就是在这个时候创建这个匿名共享内存的了: sp<Connection>connection=newConnection(inputChannel); status_tstatus=connection->initialize(); 我们来看一下这个initialize函数的实现,它定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::initialize(){ ...... intashmemFd=mChannel->getAshmemFd(); intresult=ashmem_get_size_region(ashmemFd); ...... mAshmemSize=(size_t)result; mSharedMessage=static_cast<InputMessage*>(mmap(NULL,mAshmemSize, PROT_READ|PROT_WRITE,MAP_SHARED,ashmemFd,0)); ...... mPinned=true; mSharedMessage->consumed=false; returnreset(); } InputPublisher的成员变量mChannel就是指注册在InputDispatcher中的Server端InputChannel了。我们知道,这个InputChannel除了拥有一个反向管道的读端文件描述符和一个前向管道的写端文件描述符之后,还有一个匿名共享文件描述符,这个匿名共享文件描述符就是用来创建匿名共享内存mSharedMessage的了。 这个匿名共享内存mSharedMessage的作用是什么呢?原来,在InputChannel中,前向管道和反向管道的作用只是用来在Server端和Client端之间相互通知有事件发生了,但是具体是什么样的事件,还需要去读取这个匿名共享内存的内容才知道。前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 14中(InputChannel.openInputChannelPair)创建Server端和Client端的InputChannel对时,创建一个匿名共享内存,这个匿名共享内存有两个文件描述符同时指向它,其中一个放在Server端的InputChannel中,另外一个放在Client端的InputChannel中。这样,当InputDispatcher通过Server端的InputChannel的前向管道来通知Client端有键盘事件发生时,Client端只要通过它的InputChannel中的匿名共享内存文件描述符去读取匿名共享内存中的内容,就可以知道发生了什么事情了。 回到Step 17中,接下来就是调用InputPublisher的成员函数sendDispatchSignal来通知Activity窗口处理键盘事件了。 Step 19.InputPublishe.sendDispatchSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::sendDispatchSignal(){ ...... mWasDispatched=true; returnmChannel->sendSignal(INPUT_SIGNAL_DISPATCH); } 这个函数很简单,它通过调用内部成员变量mChannel的sendSignal函数来通知相应的Activity窗口来处理键盘事件。 Step 20. InputChannel.sendSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputChannel::sendSignal(charsignal){ ssize_tnWrite; do{ nWrite=::write(mSendPipeFd,&signal,1); }while(nWrite==-1&&errno==EINTR); if(nWrite==1){ ...... returnOK; } return-errno; } 这里所谓的发送信号通知,其实是通过向其内部一个管道的写端写入一个字符来实现的。前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),它把一个InputChannel注册到应用程序主线程中的Looper对象中,然后应用程序的主线程就通过这个Looper对象睡眠等待在这个InputChannel中的前向管道中有新的内容可读了,这里的mSendPipeFd就是对应这个前向管道的写端。现在既然向这个前向管道的写端写入新的内容了,于是,应用程序的主线程就被唤醒了。 在前面分析应用程序注册键盘消息接收通道过程的Step 21中,我们也说过,当应用程序的主线程因为这个InputChannel中的前向管道的写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966636,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(5)

Step 19. EventHub.openPlatformInput 这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中: boolEventHub::openPlatformInput(void) { ...... res=scanDir(device_path); if(res<0){ LOGE("scandirfailedfor%s\n",device_path); } returntrue; } 这个函数主要是扫描device_path目录下的设备文件,然后打开它们,这里的变量device_path定义在frameworks/base/libs/ui/EventHub.cpp文件开始的地方: staticconstchar*device_path="/dev/input"; 在设备目录/dev/input中,一般有三个设备文件存在,分别是event0、mice和mouse0设备文件,其中,键盘事件就包含在event0设备文件中了。 Step 20.EventHub.scanDir 这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中: intEventHub::scanDir(constchar*dirname) { chardevname[PATH_MAX]; char*filename; DIR*dir; structdirent*de; dir=opendir(dirname); if(dir==NULL) return-1; strcpy(devname,dirname); filename=devname+strlen(devname); *filename++='/'; while((de=readdir(dir))){ if(de->d_name[0]=='.'&& (de->d_name[1]=='\0'|| (de->d_name[1]=='.'&&de->d_name[2]=='\0'))) continue; strcpy(filename,de->d_name); openDevice(devname); } closedir(dir); return0; } 根据上面一步的分析,这个函数主要就是调用openDevice函数来分别打开/dev/input/event0、/dev/input/mice和/dev/input/mouse0三个设备文件了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/p13,如需转载请自行联系原作者

优秀的个人博客,低调大师

[unity for android]第三人称视角研究

本次学习: 1.第三人称视角研究 2.摇杆区域制作 3.视角限制设计 4.简单的战斗部分 1.第三人称视角研究 第三人称视角是什么大家玩玩游戏就懂了-0- 羽化不是专业制作人,只是一个玩家,下面是羽化玩家身份的总结出来的一些经验,很多词汇非专业,见谅。。。 1.人物移动 玩过魔兽世界的玩家可能都知道W A S D可以控制人物往8个方向移动,但人的面向不会改变,意味着有左移右移和后退等动作,羽化认为这样设计比较有真实感,而且效果很好,但有些游戏比如神鬼寓言就有所改变,W A S D不仅可以控制移动方向,而且还能改变人的面向,这种设计更加灵活,适合ACT,不适合ARPG,所以羽化这是做的一个ARPG,使用的是魔兽世界的移动标准,摇杆第一次点进区域就可自由移动,原来羽化就是这么想的,看到混乱与秩序的摇杆后觉得更加合理,所以就借鉴了下。 2.视角移动 ARPG也有很多种视角,有斜45°类似火炬之光这种,也有魔兽世界一样360°的,羽化原来做过斜45°的Demo,固定视角可以解决很多问题,但也伴随着更棘手的问题,既然公司要求,所以就做了这个360°的视角转换,包括手势放大缩小。视角改变很容易,但当视角更改以后问题出现了,玩过魔兽世界包括众多自由视角游戏的玩家都知道视角虽然可以随便移动,但存在着很多约束,比如你向上滑动时,视角不可能穿过地面而到地下,一定会沿着人的方向放大,这是前人给我们的经验,比如有一个物体挡在人物和玩家中间时,视角应该拉近,这样就不会产生视觉死角,诸如此类的情况很多,就运用到了射线Ray,进入我们今天的主题。 大家可以看到,羽化这个Demo用的东西也不多,这是前期的一个Demo,还不算完整,如果有机会给大家分享下羽化现在的Demo。 2.摇杆区域制作 触摸屏发展至今,羽化见过最好大的移动摇杆莫过于“混乱与秩序”的移动摇杆了,本身摇杆制作并不困难,主要是学习下这种为玩家带来方便的思维模式,先把代码送上: Rocker.js: [javascript] view plain copy print ? //移动速度 varSpeed:float=0.05; //视角转动速度 varSpeed_L:float=0.1; //角色 varRole:Transform; //人称视角 varPlayer_true:Transform; //攻击预设 varAttackPrafab:Transform; //攻击方向 varAttackRange:Transform; //第一点按下的初始点 privatevarPO_X:int=0; privatevarPO_Y:int=0; //第二点按下的初始点 privatevarPT_X:int=0; privatevarPT_Y:int=0; //判断人是否移动 staticvarPR_M:boolean=false; //触摸点的记录 privatevarPM_X:int=0; privatevarPM_Y:int=0; //第二点按下的初始点 privatevarPS_X:int=0; privatevarPS_Y:int=0; //触摸点与中心点的距离 privatevarM_X:int=0; privatevarM_Y:int=0; //中心的坐标 privatevarPC_X:int=0; privatevarPC_Y:int=0; //旋转变量 privatevartouchDeltaPosition:Vector2; //两点静态和动态距离 privatevarDistance:int=0; privatevarDistance_D:int=0; //记录摄像机距离 staticvarCamera_Record:float=-2.0; //两点的角度 staticvarAngles:int=0; //战斗焦点 staticvarFocus:boolean=false; //打击点 privatevarhit:RaycastHit; //敌人方位 privatevarEnemie:Vector3; //攻击模式范围 privatevarRange:float=6.0; //记录时间 privatevarCreationTime:double=-10.0; //处于攻击动作 privatevarAttacking:boolean=false; //Test staticvarTest=0.0; functionUpdate() { //记录游戏时间 //Test=Time.time; //战斗状态 if(Focus) { if(Vector3.Distance(transform.position,Enemie)>Range) { Focus=false; Camera.main.depth=-1; return; } Camera.main.depth=-3; transform.LookAt(Enemie); Player_true.transform.LookAt(Enemie); } //攻击预设 if(Time.time>(CreationTime+0.5)&&Role.animation.IsPlaying("Attack")&&Attacking) { Instantiate(AttackPrafab,AttackRange.transform.position,Quaternion.identity); Attacking=false; } //单点触摸时 if(Input.touchCount==1) { Player_Move(); Attack(); //退出 if(Input.GetTouch(0).position.y>Screen.height-50&&Input.GetTouch(0).position.x>Screen.width-50) { Application.Quit(); } } //多点触摸时 elseif(Input.touchCount==2) { Player_Move(); Player_Look(); Attack(); } } //角色移动和静态视角 functionPlayer_Move() { switch(Input.GetTouch(0).phase) { caseTouchPhase.Began: PO_X=Input.GetTouch(0).position.x; PO_Y=Input.GetTouch(0).position.y; PC_X=PO_X; PC_Y=PO_Y; varray=Camera.main.ScreenPointToRay(Input.GetTouch(0).position); if(Physics.Raycast(ray,hit)) { if(hit.collider.gameObject.tag=="Enemies") { Focus=true; Enemie=hit.transform.position; } elseif((PO_X>200||PO_Y>200)&&(PO_X<Screen.width-100||PO_Y>100)) { Focus=false; Camera.main.depth=-1; } } break; caseTouchPhase.Moved: PM_X=Input.GetTouch(0).position.x; PM_Y=Input.GetTouch(0).position.y; if(PO_X<200&&PO_Y<200&&PO_X>0&&PO_Y>0) { PR_M=true; } elseif(Input.touchCount==1) { touchDeltaPosition=Input.GetTouch(0).deltaPosition; //视角限制 if(Player_true.transform.localEulerAngles.x<=60||Player_true.transform.localEulerAngles.x>=310) { Player_true.transform.Rotate(Vector3(touchDeltaPosition.y,touchDeltaPosition.x,0)*Time.deltaTime*100*Speed_L,Space.World); } elseif(Player_true.transform.localEulerAngles.x<310&&Player_true.transform.localEulerAngles.x>200) { Player_true.transform.localEulerAngles.x=311; } elseif(Player_true.transform.localEulerAngles.x>60&&Player_true.transform.localEulerAngles.x<200) { Player_true.transform.localEulerAngles.x=59; } Player_true.transform.localEulerAngles.z=0; } break; caseTouchPhase.Ended: PO_X=0; PO_Y=0; PM_X=0; PM_Y=0; PC_X=0; PC_Y=0; PR_M=false; Role.animation.Stop("Run"); Role.animation.Stop("Back"); Role.animation.Stop("Right"); Role.animation.Stop("Left"); Role.animation.PlayQueued("Wait",QueueMode.CompleteOthers); break; } if(PR_M) { M_X=PM_X-PC_X; M_Y=PM_Y-PC_Y; //求距离 Distance=Mathf.Sqrt((M_X*M_X)+(M_Y*M_Y)); //求角度 Angles=Mathf.Atan2(M_X,M_Y)*Mathf.Rad2Deg; if(Distance>=50) { PC_X=PC_X*15/16+PM_X/16; PC_Y=PC_Y*15/16+PM_Y/16; } //最大速度限制 if(M_X>40) { M_X=40; } elseif(M_X<-40) { M_X=-40; } if(M_Y>50) { M_Y=50; } elseif(M_Y<-30) { M_Y=-30; } //移动判断和优化 if(Angles>=-45&&Angles<=45) { Role.animation.CrossFade("Run"); if(Angles>=-20&&Angles<=20) { M_X=0; } } elseif(Angles>=135||Angles<=-135) { Role.animation.CrossFade("Back"); if(Angles<=-160||Angles>=160) { M_X=0; } } elseif(Angles>45&&Angles<135) { Role.animation.CrossFade("Right"); if(Angles>=70&&Angles<=110) { M_Y=0; } } elseif(Angles>-135&&Angles<-45) { Role.animation.CrossFade("Left"); if(Angles>=-110&&Angles<=-70) { M_Y=0; } } //转身 transform.localRotation=Quaternion.Euler(0,Player_true.transform.localEulerAngles.y,0); //vartarget=Quaternion.Euler(0,Player_true.transform.localEulerAngles.y,0); //transform.rotation=Quaternion.Slerp(transform.rotation,target,Time.deltaTime*2); //移动 transform.Translate(M_X*Time.deltaTime*Speed,0,M_Y*Time.deltaTime*Speed); } } //游戏移动视角 functionPlayer_Look() { switch(Input.GetTouch(1).phase) { caseTouchPhase.Began: PT_X=Input.GetTouch(1).position.x; PT_Y=Input.GetTouch(1).position.y; M_X=PO_X-PT_X; M_Y=PO_Y-PT_Y; Distance=Mathf.Sqrt((M_X*M_X)+(M_Y*M_Y)); break; caseTouchPhase.Moved: PS_X=Input.GetTouch(1).position.x; PS_Y=Input.GetTouch(1).position.y; if(PR_M&&!Focus) { touchDeltaPosition=Input.GetTouch(1).deltaPosition; Player_true.transform.Rotate(Vector3(0,touchDeltaPosition.x,0)*Time.deltaTime*160*Speed_L,Space.World); Player_true.transform.Rotate(Vector3(touchDeltaPosition.y,0,0)*Time.deltaTime*90*Speed_L,Space.World); Player_true.transform.localEulerAngles.z=0; } elseif((PO_X>200||PO_Y>200)&&(PT_X>200||PT_Y>200)) { M_X=PS_X-PM_X; M_Y=PS_Y-PM_Y; Distance_D=Mathf.Sqrt((M_X*M_X)+(M_Y*M_Y)); if(Distance-Distance_D>20&&Camera.main.transform.localPosition.z<=0) { Camera.main.transform.localPosition.z+=0.1; Camera_Record=Camera.main.transform.localPosition.z; Distance=Distance_D; } elseif(Distance-Distance_D<-20&&Camera.main.transform.localPosition.z>=-4) { Camera.main.transform.localPosition.z-=0.1; Camera_Record=Camera.main.transform.localPosition.z; Distance=Distance_D; } } break; caseTouchPhase.Ended: PT_X=0; PT_Y=0; PS_X=0; PS_Y=0; Distance=0; Distance_D=0; break; } } //攻击判断 functionAttack() { //普通攻击 if(Input.GetTouch(0).position.y<100&&Input.GetTouch(0).position.x>Screen.width-100&&!(Role.animation.IsPlaying("Attack"))) { Role.animation.Play("Attack"); Role.animation.CrossFadeQueued("Wait",0.3,QueueMode.CompleteOthers); CreationTime=Time.time; Attacking=true; } //移动攻击 elseif(PR_M&&Input.GetTouch(1).position.y<100&&Input.GetTouch(1).position.x>Screen.width-100&&!(Role.animation.IsPlaying("Attack"))) { PR_M=false; Role.animation.CrossFadeQueued("Attack",0.3,QueueMode.PlayNow); Role.animation.CrossFadeQueued("Wait",0.3,QueueMode.CompleteOthers); CreationTime=Time.time; Attacking=true; } } functionOnGUI() { GUI.Box(Rect(0,Screen.height-200,200,200),"Rocker"); GUI.Box(Rect(Screen.width-50,0,50,50),"Quit"); GUI.Box(Rect(Screen.width-100,Screen.height-100,100,100),"Attack"); } 啊,好长,其实也不多,就300行+,上面有羽化的一些注释,大家可以看看这个脚本,Rocker是绑在Player上的,大家可以从上一张图看到,羽化见了两个Player,一个叫Player_True的物体其实是一个空物体里面放置了一些基本东西包括光和摄像机,旋转视角的时候就是旋转的Player_True,其中的好处只有用过的人才能了解吧~ ~。羽化使用了预设制作攻击,这是个权益之计,以后说不定会改,这里最多判断了两个点的触控情况,羽化是分别判断的,这是吸取了前一个游戏的经验,这样不会乱,因为移动,攻击,转视角全部如果写在一个判断里面会造成很多冲突,羽化深有体会。 3.视角限制设计 前面部分羽化用不了半天就完成了,后面的视角限制花了将近2天时间,当然个人能力有限也是原因吧。这是我这次博客的重头,如何运用射线,我们主要说说Physics.Raycast这个脚本的应用,在Unity帮助文档里面提到了,这里羽化尝试了下,最好使用Raycast (origin :Vector3,direction :Vector3,out hitInfo :RaycastHit,distance : float =Mathf.Infinity,layerMask : int = kDefaultRaycastLayers),这个方法,别的不一定靠谱。。。有的地面会莫名其妙穿过去。 CameraZoom.js: [javascript] view plain copy print ? varUp:Transform; privatevarForward:boolean=false; privatevarBack:boolean=false; functionUpdate() { //通过向下和前后激光判断视角 varlayerMask=1<<2; layerMask=~layerMask; varhit:RaycastHit; //向前激光 判断中间阻挡的镜头靠近 if(Physics.Raycast(transform.position,Camera.main.transform.TransformDirection(Vector3.forward),hit,(-Camera.main.transform.localPosition.z))) { Forward=true; vardistanceToForward=hit.distance; if(Camera.main.transform.localPosition.z<-0.5) { Camera.main.transform.localPosition.z+=0.05; } } else { Forward=false; } //向后激光 判断后退时靠墙的镜头拉近 if(Physics.Raycast(transform.position,Camera.main.transform.TransformDirection(Vector3.forward*(-1)),hit,0.2,layerMask)) { Back=true; vardistanceToBack=hit.distance; if(distanceToBack>0.1) { Back=false; } } //向下激光 用于判断下拉镜头的拉近 if(Physics.Raycast(transform.position,-Vector3.up,hit,(-2)*Rocker.Camera_Record,layerMask)) { vardistanceToGround=hit.distance; //print(distanceToGround); } if(distanceToGround<0.1) { Camera.main.transform.localPosition.z+=0.1; } elseif(Camera.main.transform.localPosition.z>Rocker.Camera_Record&&distanceToGround>0.2&&!Back&&!Forward) { Camera.main.transform.localPosition.z-=0.02; Camera.main.transform.localPosition.y=0.6; } if(!Rocker.Focus) { transform.LookAt(Up); } } functionOnTriggerStay(other:Collider) { if(/*other.attachedRigidbody&&*/Camera.main.transform.localPosition.z<-0.4) { Camera.main.transform.localPosition.z+=0.02; } Back=true; } functionOnTriggerExit(other:Collider) { Back=false; } 首先,羽化开始认为只有一个射线就可以搞定了,结果一个射线什么都判断不了,后来羽化仔细想了想就写成了三个射线,加上一个判断,终于效果出现了,话说羽化在摄像机设置Collider,是怕一个不小心,玩家把整个地图看透了-0- 如果你想视角更舒畅点可以把other.attachedRigidbody的注释去掉。这里有个函数layerMask是判断射线穿过那个层的,以后肯定用得上,1<<2代码射线只能穿过前两层。 4.简单的战斗部分 战斗部分很简单,做一个攻击预设,触发攻击动作时就产生预设,在极短时间给怪物造成伤害。绝大多数代码都在移动部分了,这里看看一个简单的AI。 AI.js: [javascript] view plain copy print ? varrange=4; varPlayer:Transform; privatevarhealth=10; functionUpdate() { Discover(); } functionOnTriggerEnter(other:Collider) { if(other.attachedRigidbody) { if(other.gameObject.tag=="Attack") { Rocker.Test=10.2; transform.Find("Small").animation.CrossFade("Hit"); transform.Find("Small").animation.CrossFadeQueued("Wait",0.3,QueueMode.CompleteOthers); health--; if(health<=0) { transform.Find("Small").animation.CrossFade("Dead"); Rocker.Focus=false; Camera.main.depth=-1; transform.tag="Dead"; } } } } functionDiscover() { varlayerMask=1<<3; layerMask=~layerMask; if(Vector3.Distance(transform.position,Player.position)>range) { returnfalse; } varhit:RaycastHit; if(Physics.Linecast(transform.position,Player.position,hit,layerMask)) { if(hit.collider.gameObject.tag=="Player"&&!(transform.Find("Small").animation.IsPlaying("Dead"))) { transform.LookAt(Player); returntrue; } else { returnfalse; } } returntrue; } 怪物在一定范围内会发现你,面朝向敌人,简单的AI,可以添加怪物行走等,这就看大家对自己什么要求了,当玩家点击怪物后会进入战斗视角,这时的移动时围绕怪物的,是不是很像Fable~ ~羽化的Demo就初步完成了,最后来张截图。 自己认为视角做得很棒,有什么不足还望指出~ ~ 本文转自羽化:http://blog.csdn.net/libeifs/article/details/6696424 APK下载地址: http://dl.dbank.com/c0bbqt1p13 本文转蓬莱仙羽51CTO博客,原文链接:http://blog.51cto.com/dingxiaowei/1366370,如需转载请自行联系原作者

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册