精华内容
下载资源
问答
  • WOW模型修改-山口山

    2009-04-29 10:46:18
    WOW模型修改-山口山,修改人物、种族、技能、物品模型
  • WOW里面模型修改 主要用于一些地域 物品以及人物身上的模型变化 从而让物体更加突出特点更加容易被辨识
  • wow镜头模拟

    千次阅读 2011-12-05 20:02:00
    其它方面,比如wow中有个选项,主角移动的时候镜头会自动旋转到人物后方;还有就是镜头因为场景碰撞离主角很近以后,当碰撞没有的时候,会自动退回到原始的跟随距离等。这里将分析并实现以上描述的5点。 接...

            3D游戏编程中,镜头的控制相当重要,不同的镜头表现,能给玩家完全不同的体验;比如《跑跑卡丁车》中的跟随镜头,每当甩尾的时候,镜头也会有相应的运动轨迹,如果只是单单的垂直俯视,那肯定全无甩尾的感觉。废话少说,这里分析下魔兽世界中主角的跟随镜头行为,因为这个跟随模型很简单,网上也很少有镜头跟随模型相关的文章,希望能起到个抛砖引玉的效果。阅读本文需要有一定3D图形学基础。


     在wow中,主角的镜头主要可以分解为3种运动,这3中运动相互独立,为编程带来了方便:

    1.镜头forward方向的进、退,根据鼠标滚轮控制

    2.绕主角垂直中心轴的转动,鼠标按住左键后左右移动控制

    3.绕某水平轴的转动,鼠标按住左或右键后上下移动控制

    玩过wow的朋友可以细想一下,主要方式是不是就这三种。其它方面,比如wow中有个选项,主角移动的时候镜头会自动旋转到人物后方;还有就是镜头因为场景碰撞离主角很近以后,当碰撞没有的时候,会自动退回到原始的跟随距离等。这里将分析并实现以上描述的5点。


    接下来做一些约定:

    世界坐标的垂直向上方向为Y轴正方向

    同时主角的Y轴始终与世界Y轴一致,主角可在XOZ平面内旋转

    主角的朝向为其Z轴正方向,主角的X轴和Z轴始终在XOZ平面内,即其y分量为0

    镜头的朝向为其Z轴正方向,镜头的X轴始终水平,在XOZ平面内

    镜头与场景采用球形碰撞


    不太明白的可以看示意图:


                                                  相机跟随模型侧视图 

    侧视图中,椭球体代表主角,点T是主角的Root Bone位置,一般为脚底;点P是相机位置;点A是主角垂直中心轴上一个点,相机始终盯着这个点



                                                相机跟随模型俯视图,对应3种运动的第2种

    俯视图中可以看到,主角的Z轴代表其正前方,相机的Z轴代表其观察方向


    在这里,相机的运动其实就是向量P->A的运动:

    P->A缩短、变长就是运动方式1

    P->A绕主角垂直中心轴360°无限制旋转就是运动方式2,见俯视图

    P->A绕经过A点的某水平轴有限制旋转就是运动方式3

    下面看下运动方式3的有限制旋转示意图:


                                                 相机俯仰角示意图,对应3种运动的第3种

    theta角为向量P->A上下旋转的最大限制角,可以设定为上下对称,也可以设定为不对称(不过需要另外写代码判断,这里不做分析)

            为简化编程,我们将向量P->A的运动在跟随目标(主角)的局部坐标系内先实现,再转换到世界坐标系。跟随目标的X轴和Z轴见‘相机跟随模型俯视图’,Y轴见‘相机跟随模型侧视图’,在此局部坐标系中,点T成为坐标系原点


            鼠标的运动可以分为三种:左右移动,上下移动和滚轮滚动,这里分别叫做delta X, delta Y,delta Z,简称 dx, dy, dz,dx、dy的符号跟屏幕坐标系定义有关,这些参数可以从系统处获取,这里不讲。

    鼠标左右移动的时候,dx不为0,向左dx < 0,向右dx > 0

    鼠标上下移动的时候,dy不为0,向上dy < 0,向下dy > 0

    鼠标滚动轮动的时候,dz不为0


            下面根据实现代码逐步分析,代码是用python写的,熟悉c/c++的朋友很容易看懂,代码复制下来并不能直接运行,因为依赖其它模块,要用到自己游戏中需要做些修改

    先定义一个类CMouseInputEvent,上层逻辑将镜头用到的相关输入通过该类传递给镜头控制类:

    KEY_PAD = 0x01

    KEY_TRG = 0x02

    KEY_RLS = 0x04

    #KEY_PAD表示按键当前是按下状态,KEY_TRG表示按键当前有按下动作,KEY_RLS表示按键当前有松开动作

    #下面的lbutton、rbutton、mbutton都是KEY_PAD/KEY_TRG/KEY_RLS按位或的组合


    class   CMouseInputEvent:
        def __init__(self):     #构造函数
            self.lbutton = 0    #left   button
            self.rbutton = 0    #right  button
            self.mbutton = 0    #midden button
            self.mousedx = 0
            self.mousedy = 0
            self.mousedz = 0
        
        def setMouse(self, lbt, rbt, mbt, dx, dy, dz):
            self.lbutton = lbt
            self.rbutton = rbt
            self.mbutton = mbt
            self.mousedx = dx
            self.mousedy = dy
            self.mousedz = dz
    
    
    class   CFollowCameraAI:
        def __init__(self, cmr, scn, target, ofst, lkatofst):
            self.UnitY = math3d.vector(0, 1, 0)  #向上的Y轴
            self.input    = CMouseInputEvent()
            self._camera = cmr                       #场景camera引用
            self._scene = scn                          #场景引用
            #跟随目标
            self.followTarget = target              #跟随目标引用
            self.cameraOffset = ofst               #camera位置相对跟随目标的偏移,向量T->P,局部坐标系参数
            self.lookatOffset = lkatofst            #camera lookAt的点相对跟随目标的偏移,向量T->A,局部坐标系参数
            #控制参数
            self.minOffsetLen = 5.0                #最小跟随距离,向量P->A的最短距离
            self.maxOffsetLen = 168.0           #最大跟随距离,向量P->A的最长距离
            self.limitTgn = math.tan(1.4)        #最大俯视/仰视角度(1.4弧度大约为80°)
            #速度控制
            self.x_scroll_speed = 0.008         #X,左右方向旋转速度,控制向量P->A绕目标Y轴旋转
            self.y_scroll_speed = -0.0022     #Y,上下方向旋转速度,控制向量P->A绕H平面旋转
            self.z_scroll_speed = 0.05           #Z,前后方向滚动速度,控制向量P->A长短
            self.rotback_angular_speed = 0.1     #自动回转速度,控制同x_scroll_speed
            self.z_backaway_speed = 40.0         #轴向位置插值速度,控制同z_scroll_speed
            
            #碰撞检测
            self.radius = 0.8                            #camera采用球形碰撞,这是碰撞球半径
            self.collided_shorten = False      #某内部状态标志位
            u = math3d.vector(1, 1, 1) * self.radius
            self._col_shape = collision.col_object(collision.SPHERE, u)    #构建球形碰撞体
        
        
        def Update(self, dTime, bTargetMoving, bRotBackEnabled):
            #dTime,帧时间,单位秒
            #bTargetMoving, bool, 目标是否在移动
            #bRotBackEnabled, bool, 是否允许camera横向转回到跟随目标背后
            
            rot_back_request = bTargetMoving                      #跟随目标移动的时候才转到背后,停下的时候不会转
            LookAt = self.lookatOffset - self.cameraOffset    #向量P->A
            
            #远近控制
            if self.input.mousedz != 0:
                curLength = abs(LookAt)          #向量当前长度
                LookAt.normalize()
                #根据滚轮位移计算需要缩放向量P->A多少距离,并限定其范围
                curLength -= self.input.mousedz * self.z_scroll_speed
                if curLength > self.maxOffsetLen:
                    curLength = self.maxOffsetLen
                elif curLength A绕目标Y旋转一定角度
                    #横向旋转矩阵
                    upRot = math3d.matrix.make_rotation(self.UnitY, self.input.mousedx * self.x_scroll_speed)
                    LookAt *= upRot              #旋转向量P->A,横向
            
            #上下控制
            if self.input.mousedy != 0:
                #左中右三键都可以控制上下方向
                if ( ((self.input.lbutton & KEY_PAD) and (not (self.input.lbutton & KEY_TRG)))
                    or ( (self.input.rbutton & KEY_PAD) and (not (self.input.rbutton & KEY_TRG)))
                    or ( (self.input.mbutton & KEY_PAD) and (not (self.input.mbutton & KEY_TRG))) ):
                    
                    #计算纵向旋转轴,该轴与目标Y和Z垂直;相当于目标将其Z方向旋转到向量P->A的水平分量方向
                    right = LookAt.cross(self.UnitY)
                    right.normalize()
                    #纵向旋转矩阵
                    rightRot = math3d.matrix.make_rotation(right, self.input.mousedy * self.y_scroll_speed)
                    
                    temp = LookAt * rightRot     #旋转向量P->A,纵向
                    
                    #纵向旋转后需要做俯仰角限定
                    #先计算当前水平分量对应的最大垂直分量
                    maxY =  math.sqrt(temp.x * temp.x + temp.z * temp.z) * self.limitTgn
                    if temp.y < -maxY:
                        temp.y = -maxY
                    elif temp.y > maxY:
                        temp.y = maxY
                    
                    temp.normalize()
                    LookAt = temp * abs(LookAt)  #最终方向向量 * 向量P->A原始长度
            
            
            #放开左键后,自动转到目标背面
            if bRotBackEnabled and rot_back_request and not (self.input.lbutton & KEY_PAD):
                dst = math3d.vector(0, 0, -1)    #要转到哪根轴(跟随目标的局部坐标系,正前方是(0,0,1),背后就是(0,0,-1))
                src = math3d.vector(LookAt.x, 0, LookAt.z)    #当前轴水平分量
                src.normalize()                  #src归一化,方便计算,dst也是归一化向量
                t =  src.cross(dst)              #src转到dst,因此旋转轴是src叉乘dst,因为是两个水平向量叉乘,结果应该是垂直方向的向量
                cp = t.dot(self.UnitY)         #cp = t.y = sin(theta),src、dst、self.UnitY的混合积
                dp = src.dot(dst)                #计算src到dst的夹角,dp = cos(theta);当src与dst重合时,dp == 1或-1;当src与dst垂直时,dp == 0
                abcp = abs(cp)                  #因为是src叉乘dst,sin(theta)有正负之分;当src与dst重合时,cp == 0;当src与dst垂直时,cp == 1
                #非尾部且非夹角很小;当dp> 0时说明src与dst夹角小于90°,即src在主角背后;当dp<0时src在主角前方
                if  not ((abcp < 0.1) and (dp > 0)):
                    r = self.rotback_angular_speed
                    if abcp >= 0.1:     #非正向且非夹角很小
                        r *= cp / abcp
                        if abs(r) > abcp:
                            r = -cp          #旋转角度比实际夹角小
                    yRotBack = math3d.matrix.make_rotation(self.UnitY, r)
                    LookAt *= yRotBack
            
            #计算需求位置
            mat = self.followTarget.rotation_matrix               #跟随目标的方向矩阵
            mat.translation = self.followTarget.position         #跟随目标的位置,点T
            fwd = mat.mulvec3x3(LookAt)                              #fwd是LookAt的世界坐标,将最终变成camera的方向向量
            self.cameraOffset = self.lookatOffset - LookAt   #self.cameraOffset的局部坐标(局部坐标-局部向量)
            ofst = self.cameraOffset * mat                             #ofst是self.cameraOffset的世界坐标,点P
            
            #碰撞检测,需要在世界坐标系里执行
            start = self.lookatOffset * mat                 #start是self.lookatOffset的世界坐标,点A
            delta = ofst - start                                    #ofst是self.cameraOffset的世界坐标,点P
            #由近及远插值
            if self.collided_shorten:                           #如果相机在自己z轴向遇到过碰撞,即向量P->A当前长度可能小于预定长度,则缓慢插值到预定长度
                curOfstLen = abs(self._camera.position - start)
                reqOfstLen = abs(delta)
                if curOfstLen < reqOfstLen:
                    curOfstLen += self.z_backaway_speed * dTime * ((bTargetMoving and 2.0) or 1.0)
                    if curOfstLen > reqOfstLen:
                        curOfstLen = reqOfstLen
                    delta.normalize()
                    delta *= curOfstLen
                    ofst = start + delta
                else:
                    self.collided_shorten = False
            
            hit, hit_point, normal, tt, color, objs = self._scene.col.sweep_test(self._col_shape, start, start + delta)
            if hit:
                self.collided_shorten = True                    #上次曾碰到过,记个标志位
                ofst = hit_point + normal * self.radius     #沿碰撞表面法线外移radius,减少camera穿面情况
            
            #设定最终方位
            self._camera.set_placement(ofst, fwd, mat.up)   #camera pos, camera forward vector, camera up vector

    有不明白的地方可以交流,也欢迎更好的建议。




    展开全文
  • 今天看到新闻,魔兽世界最新的资料片《潘达利亚的迷雾》就要在十月二日上线了。这次中国大陆服务器总算是有机会版本与全球同步,和世界上其他地区的...这个版本解决了FBX动画导出最后几帧模型扭曲的问题,对我等不会3D
    今天看到新闻,魔兽世界最新的资料片《潘达利亚的迷雾》就要在十月二日上线了。这次中国大陆服务器总算是有机会版本与全球同步,和世界上其他地区的玩家在Raid进度上一决高下。

    作为一名几乎没有存在感的业余玩家,好像跟我也没有什么关系。 倒是Wow Model Viewer的作者之前有言,最新的v8.0.0.0版本将会在熊猫人后发布。这个版本解决了FBX动画导出最后几帧模型扭曲的问题,对我等不会3D建模的程序员来说那是大大的福音。

    尽管目前WMV的导出功能还不是很完美,不过也还可以凑合着用。刚好之前找到个模仿WOW镜头的Unity素材包,这次就利用WMV的导出英雄榜人物功能,让自己的WOW角色奔跑在自己的游戏里吧。^_^


    ▇ 准备工作

    点击这里下载最新版本的Wow Model Viewer;

    • 下载Unity仿WOW镜头包,稍后提供;
    导入镜头包(WoW_controller_and_MiniMap_Package.unitypackage),作者已经很体贴地提供了Demo场景,打开WowCharacterMovementAndCamera/Scenes/Demo Scene。运行游戏,可以直接操作这个角色(这完全就是个球啊)进行移动。我稍微实验了下,只能说它只实现了WOW里的部分操作,一些细节上的东西还是有点不一样。比如无法通过拉近镜头切换到第一人称视角,无法用左键观察周围环境等等。

    img?s=v5pdCz9Wa&l=y.jpg 

    接下来要通过WMV导出角色模型,首先要从魔兽世界英雄榜里找到自己的角色,如果没有也没关系,在WMV中可以自己定制。中国大陆的英雄榜链接在这里:http://www.battlenet.com.cn/wow/zh/,只要在右上角里的搜索里搜索出自己的角色页面就可以
    了。比如我的是(请无视我的装备,谢谢XD):

    img?s=3tA2agsY6&l=y.jpg 

    复制这个页面的链接,然后打开WMV,选择主菜单→角色→导入英雄榜角色,在弹出的输入框中粘贴,点击OK。等待几秒后人物和装备就被下载下来了。如果没有角色,也可以在左边选择Character下面的模型,自定义角色样式。注意目前这个版本的WMV只能导出角色带盔甲的模型和动作,所以武器附魔之类的就只能自己YY了……

    定制好角色之后,选择主菜单→导出模型→FBX。经过漫长的等待之后就可以得到FBX文件和贴图了。把这些资源文件丢到Unity的工程面板中,并找到Materials下面的材质,修改好Shader,我这里用的是Transparent/Coutout/Soft Edge Unlit。

    关闭Demo场景,打开自己练习用的ElwynnForest场景,在工程面板找到WowCharacterMovementAndCamera/Prefabs里的Player和MainCamera并拖入设计视图。于是就有了和Demo场景中一样的效果,不过还不够,因为那个小圆球根本没有我们需要的动画。所以必须动个小手术:

    • 把自己的模型拖入Player,删除原有的CharacterWithAnimation(这会导致游戏对象与预制失去连接);
    • 从Scripts里把AnimationController重新附加到刚拖入的模型上(我这里叫BloodElfMale);
    • 设置Animation Controller里所需要的动画名称,见下图;
    • 由于WMV的导出动画BUG,所以还要为这个模型附加上一章节中写的WowAnimationFix脚本;
    • 把BloodElfMale拖到Player属性中的Animation Object上,移除原有的Mesh Filter和Mesh Renderer组件;
    • 由于导入的角色是默认向左的,所以可能还要把BloodElfMale的Rotation修改为90。


    img?s=GwKzM6haD&l=y.jpg 

    ▇ 知识点


    • AnimationController的实现比较简单,根据设置的动画名称播放相应的动画,比如走路的时候调用Walk(),里边只是判断了下动画是否存在,然后用animation.CrossFade()播放动画,用animation.Play()也可以,但是前者可以有过渡效果;
    • Physics.Raycast()可以判断两点之间是否有碰撞器,保证角色不会穿过带有碰撞器的物体;
    • Input.GetMouseButton()可以判断鼠标按键是否按下,0-左键,1-右键,2-中键;
    • Input.GetAxis()可以获取摇杆状态,”Horizontal”-水平方向,”Vertical”-垂直方向;

    ▇ 完成效果

    至此,自定义角色已经可以控制了,点击运行游戏看下效果:)

    img?s=BdYAD3t4P&l=y.jpg 
    2013-4-25 10:57:43 上传
    下载附件 (380.03 KB)


    有了自己的角色后,下一章可以试试把怪物导进来,替换那个盒子君……那么,下章见!
    展开全文
  • ANDROID应用中嵌入Unity3D视图(展示3D模型)

    万次阅读 多人点赞 2014-07-24 00:10:21
    本文通过一个在Android中展示3D模型的实例,介绍如何将Unity3d以View的形式嵌入到Android应用中。并提供Deom和代码下载。

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!

    效果展示:


     

                 


    开篇废话:

    我现在所在的Team每周需要一个人给大家介绍一个知识点,或者新技术。这礼拜正好轮到我了,由于我工作才一年,面对那帮老鸟讲知识点感觉有点作死。所以我就准备选个新技术介绍一下。

    由于我在大学里自学过一段时间Unity3D,所以我想介绍的技术就是它,但我现在做的是应用开发,不能做个小游戏去给大家演示。所以我想到比较简单,直观,而且有可能真正能用到的就是在Android应用中展示3D模型。比如在产品展示时直接把这个产品的3D模型展示出来而不是个图片,效果应该非常棒(OpenGL应该也可以做)。

    思路定下以后就发现大学时学的Unity3D的内容基本忘光了,虽然偶尔有Unity3D的文章都会点开看看,但还是得重新学。记得当时学Unity3D的时候看过一个叫雨松MOMO的博客。那时年轻,懵懂,找不到方向的我还给雨松大神发了一封邮件去请教大学应该怎么学习和做游戏相关的问题,结果人家没回,导致我更加失落于是接着学android去了。。。又扯远了。。。于是我又找到他的博客,把Unity3D基础部分的相关文章都看了一遍。

    但是他博客里有介绍如何在Unity3D中调用Android,而我想做的是在Android中调用Unity3D,而且是把Unity3D嵌套在ANDROID的视图里面。最后费了九牛二虎之力才把这个Demo做出来。


    准备工作:

    下面是我总结的流程,目的是使本文思路更加清晰一些:

    1.Android端代码可以在Eclipse中开发(AndroidStudio没有试,应该也可以)

    2.Unity3D端代码要在Unity中开发

    3.Android和Unity3D端,两边都需要加入一些代码从而可以使之关联交互。

    4.将Android端代码编译成jar文件以插件形式放入到Unity端中

    5.在Unity中将整个项目Build成apk文件,然后安装到手机或模拟器里运行

    本文主要讲解1,2,3。对于4,5建议大家去看雨松MOMO的Unity博客的第17篇和第18篇。


    UnityPlay:

    在编写Android端和Unity3d端代码前,有必要先了解一下可以使两部分交互的类UnityPlay。

    个人理解UnityPlay是个Unity提供给外部交互的一个接口类。

    为什么是“个人理解”?这我不得不爆粗口了,TMD官网根本就没有相关的API和文档(如果大家有谁找到一定给我来一份,就当我骂自己了)。

    热心的网友已经找到了:http://docs.unity3d.com/Manual/PluginsForAndroid.html。。。。


    在关联Android时,想拿到UnityPlay以及相关类的jar包可以从下面的地址找到:Unity安装路径\Editor\Data\PlaybackEngines\androidplayer\bin在bin文件夹下有一个classes.jar的jar文件,它就是我们想要的。

    而在bin同目录下有一个src文件,点击到最后有3个类,分别是UnityPlayerActivity.java,UnityPlayerProxyActivity.java,UnityPlayerNativeActivity.java。前两个打开个后只有一行代码,说的是UnityPlayerActivity和UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity。而打开UnityPlayerNativeActivity中居然有代码,而且我估计这应该是UnityPlayerNativeActivity的源码。

    由于关于UnityPlay的资料我只找到这么一个,所以我把UnityPlayerNativeActivity中的代码都贴出来,如果我注解有不对的地方希望大家指正。

    /**
     * UnityPlayerActivity,UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity
     * 而UnityPlayerNativeActivity继承自NativeActivity
     * 在该类里定义了一些和ANDROID生命周期相同的回调方法,留给自定义的Activity子类重写。
     */
    public class UnityPlayerNativeActivity extends NativeActivity
    {
    	//UnityPlayer的引用,并且我们不能改变这个引用变量的名字,它被native code所引用
    	protected UnityPlayer mUnityPlayer;
    
    	protected void onCreate (Bundle savedInstanceState)
    	{
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		super.onCreate(savedInstanceState);
    		// 设置显示窗口参数
    		getWindow().takeSurface(null);
    		setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
    		getWindow().setFormat(PixelFormat.RGB_565);
    
    		// 创建一个UnityPlayer对象,并赋值给全局的引用变量
    		mUnityPlayer = new UnityPlayer(this);
    		//为UnityPlayer设置一些参数
    		if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))
    			getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,
    			                       WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
    		int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
    		boolean trueColor8888 = false;
    		// UnityPlayer.init()方法需要在将view附加到layout之前调用。它将会调用native code
    		mUnityPlayer.init(glesMode, trueColor8888);
    		
    		// 从UnityPlayer中获取到Unity的View视图
    		View playerView = mUnityPlayer.getView();
    		// 将Unity视图加载到根视图上
    		setContentView(playerView);
    		// 使Unity视图获取焦点
    		playerView.requestFocus();
    	}
    	protected void onDestroy ()
    	{
    		// 当Activity结束的时候调用UnityPlayer.quit()方法,它会卸载之前调用的native code
    		mUnityPlayer.quit();
    		super.onDestroy();
    	}
    
    	// 下面几个方法都是ANDROID相关回调方法,确保在ANDROID执行相应方法时UnityPlayer也需调用相应方法
    	protected void onPause()
    	{
    		super.onPause();
    		mUnityPlayer.pause();
    	}
    	protected void onResume()
    	{
    		super.onResume();
    		mUnityPlayer.resume();
    	}
    	 
    	public void onConfigurationChanged(Configuration newConfig)
    	{
    		super.onConfigurationChanged(newConfig);
    		mUnityPlayer.configurationChanged(newConfig);
    	}
    	public void onWindowFocusChanged(boolean hasFocus)
    	{
    		super.onWindowFocusChanged(hasFocus);
    		mUnityPlayer.windowFocusChanged(hasFocus);
    	}
    	public boolean dispatchKeyEvent(KeyEvent event)
    	{
    		if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
    			return mUnityPlayer.onKeyMultiple(event.getKeyCode(), event.getRepeatCount(), event);
    		return super.dispatchKeyEvent(event);
    	}
    }
    
    看完这个类后就知道了为什么在自定义的Activity中继承了UnityPlayerActivity等类以后,只要重写了onCreate并调用super.onCreate()方法后不需要任何其他的代码就会自动的显示出Unity3D的视图。因为初始化Unity视图的代码都在UnityPlayerNativeActivity父类中实现了。

    ANDROID端代码:

    在写ANDROID代码的时候,一定要导入Unity3D提供给我们的jar包,jar包的位置我在上面说了。引入jar包加入到buildpath中这些最基本的我就不多说了。

    要想和Unity交互,我们就不能继承ANDROID提供给我们的Activity,我们需要继承刚才jar包中引入的Unity提供的Activity类,一共有这么3个:

    UnityPlayerActivity,UnityPlayerProxyActivity,UnityPlayerNativeActivity。具体区别不知道,因为没有文档,没有API,没有源码(这里再次鄙视一下)。刚才我们看过UnityPlayerNativeActivity的代码(虽然很短,但我觉得这个就是源码),知道UnityPlayerActivity,UnityPlayerProxyActivity都是它的子类,而且最终父类为NativeActivity。所以我们继承Unity提供的最外层的子类是最好的选择,我这里选择的是UnityPlayerActivity,因为名字最简单,觉得该封装的都应该封装好了。

    public class MainActivity extends UnityPlayerActivity {
    	
    	private Button topButton;
    	private Button bottomButton;
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		
    		// 设置test为我们的根布局
    		setContentView(R.layout.test);
    		
    		// 通过刚才的源码分析,知道mUnityPlayer为一个全局的引用变量,而且已经在父类中设置好了,所以直接拿来用就可以了
    		View playerView = mUnityPlayer.getView();
    		// 将Unity的视图添加到我们为其准备的父容器中
    		LinearLayout ll = (LinearLayout) findViewById(R.id.unityViewLyaout);
    		ll.addView(playerView);
    		
    		// 上面的button设置监听器
    		topButton = (Button) findViewById(R.id.topButton);
    		topButton.setOnClickListener(new View.OnClickListener() {
    
    			@Override
    			public void onClick(View v) {
    				//发送消息给Unity端,该函数第一个参数为接受消息的类对象,第二个该类对象用接受消息的方法,第三个参数为传递的消息
    				//所以下面的意思就为:调用Main Camera下面的Previous方法,传送的消息为空
    				UnityPlayer.UnitySendMessage("Main Camera","Previous",""); 
    			}
    		});
    		
    		// 为下面的button设置监听器
    		bottomButton = (Button) findViewById(R.id.bottomBtn);
    		bottomButton.setOnClickListener(new View.OnClickListener() {
    			
    			@Override
    			public void onClick(View v) {
    				//调用Main Camera下面的Next方法,传送的消息为空
    				UnityPlayer.UnitySendMessage("Main Camera","Next","");   
    			}
    		});
    	}
    }

    最后看一下Android端的布局文件,布局很简单,上下各有一个button按钮,两个按钮中间是Unity的视图。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <Button
            android:id="@+id/topButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:text="PREVIOUS" />
    
        <LinearLayout
            android:id="@+id/unityViewLyaout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/bottomBtn"
            android:layout_below="@+id/topButton"
            android:orientation="horizontal" >
        </LinearLayout>
    
        <Button
            android:id="@+id/bottomBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="NEXT" />
    
    </RelativeLayout>
    Android端的代码就介绍完了,很简单。唯一的难点就是UnityPlayerActivity和UnityPlayer的使用,就这两个破玩意花了我好几天的时间,很简单的东西不知道为什么官方不给个文档或者API(也可能我太挫没找到。。。)

    Unity3D端代码:

    先看一下我的项目结构:


    JavaScript存放的是脚本

    Models存放的是我在Assert Store中下载的免费的一些模型文件

    Plugins下是我的Android工程,具体做法参考网上教程(这里推荐雨松大神的第17篇)

    Prefab我是调整模型后定义的预制体

    在场景中,我只有一个摄像机,和一个直射光。将脚本绑定到摄像机上,然后将之前调整好的5个预设模型添加到脚本的相应对象中。


    下面是脚本的代码,关于模型的旋转缩放是直接用了雨松MOMO的一篇文章中的代码,然后再加上了本例中的一些逻辑而组成的。

    #pragma strict
    
    //5个模型,从外部传入
    var car : GameObject;
    var helicopter : GameObject;
    var suv : GameObject;
    var plane : GameObject;
    var tank : GameObject;
    
    //模型数组下标
    private var index : int;
    //模型数组
    private var models : GameObject[];
    //当前模型对象
    private var mCurrentGameObject : GameObject;
    
    /******************************************/
    /*分割线之下的变量用于触摸手势镜头控制旋转和缩放*/
    /******************************************/
    
    //缩放系数
    var distance = 10.0;
    //左右滑动移动速度
    var xSpeed = 250.0;
    var ySpeed = 120.0;
    //缩放限制系数
    var yMinLimit = -20;
    var yMaxLimit = 80;
    //摄像头的位置
    var x = 0.0;
    var y = 0.0;
    //记录上一次手机触摸位置判断用户是在左放大还是缩小手势
    private var oldPosition1 : Vector2;
    private var oldPosition2 : Vector2;
    
    
    function Start () {
    	//初始化模型数组
    	index = 0;
    	models = new GameObject[5];
    	models[0] = car;
    	models[1] = helicopter;
    	models[2] = suv;
    	models[3] = plane;
    	models[4] = tank;
    	//克隆一个初始模型对象
    	mCurrentGameObject = Instantiate(models[index], Vector3(0,0,0), Quaternion.Euler(-20,0,0));	
    	
    	//初始化镜头参数
    	var angles = transform.eulerAngles;
        x = angles.y;
        y = angles.x;
    }
    
    function Update () {
    
    	//判断触摸数量为单点触摸
        if(Input.touchCount == 1)
        {
            //触摸类型为移动触摸
            if(Input.GetTouch(0).phase==TouchPhase.Moved)
            {
                //根据触摸点计算X与Y位置
                x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
                y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
     
            }
        }
     
        //判断触摸数量为多点触摸
        if(Input.touchCount > 1 )
        {
            //前两只手指触摸类型都为移动触摸
            if(Input.GetTouch(0).phase==TouchPhase.Moved||Input.GetTouch(1).phase==TouchPhase.Moved)
            {
                    //计算出当前两点触摸点的位置
                        var tempPosition1 = Input.GetTouch(0).position;
                    var tempPosition2 = Input.GetTouch(1).position;
                    //函数返回真为放大,返回假为缩小
                    if(isEnlarge(oldPosition1,oldPosition2,tempPosition1,tempPosition2))
                    {
                        //放大系数超过3以后不允许继续放大
                        //这里的数据是根据我项目中的模型而调节的,大家可以自己任意修改
                           if(distance > 3)
                           {
                               distance -= 0.5;
                           }
                       }else
                    {
                        //缩小洗漱返回18.5后不允许继续缩小
                        //这里的数据是根据我项目中的模型而调节的,大家可以自己任意修改
                        if(distance < 18.5)
                        {
                            distance += 0.5;
                        }
                    }
                //备份上一次触摸点的位置,用于对比
                oldPosition1=tempPosition1;
                oldPosition2=tempPosition2;
            }
        }
    }
    
    //函数返回真为放大,返回假为缩小
    function isEnlarge(oP1 : Vector2,oP2 : Vector2,nP1 : Vector2,nP2 : Vector2) : boolean
    {
        //函数传入上一次触摸两点的位置与本次触摸两点的位置计算出用户的手势
        var leng1 =Mathf.Sqrt((oP1.x-oP2.x)*(oP1.x-oP2.x)+(oP1.y-oP2.y)*(oP1.y-oP2.y));
        var leng2 =Mathf.Sqrt((nP1.x-nP2.x)*(nP1.x-nP2.x)+(nP1.y-nP2.y)*(nP1.y-nP2.y));
        if(leng1 < leng2)
        {
             //放大手势
             return true;
        }else
        {
            //缩小手势
            return false;
        }
    }
     
    //Update方法一旦调用结束以后进入这里算出重置摄像机的位置
    function LateUpdate () {
     
        //mCurrentGameObject为我们当前模型对象,缩放旋转的参照物
        if (mCurrentGameObject.transform) {        
     
            //重置摄像机的位置
             y = ClampAngle(y, yMinLimit, yMaxLimit);
            var rotation = Quaternion.Euler(y, x, 0);
            var position = rotation * Vector3(0.0, 0.0, -distance) + mCurrentGameObject.transform.position;
     
            transform.rotation = rotation;
            transform.position = position;
        }
    }
     
    static function ClampAngle (angle : float, min : float, max : float) {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp (angle, min, max);
    }
    
    // 当android中按下next,显示下一个模型
    function Next () {
    	index = index+1;
    	if (index > models.Length-1) {
    		index = 0;
    	}
    	Debug.Log("next");
    	// 摧毁当前对象
    	Destroy(mCurrentGameObject);
    	// 建立新的模型对象
    	mCurrentGameObject = Instantiate(models[index]);
    }
    
    // 当android中按下previous,显示上一个模型
    function Previous () {
    	index = index-1;
    	if (index < 0) {
    		index = models.Length-1;
    	}
    	Debug.Log("previous");
    	// 摧毁当前对象
    	Destroy(mCurrentGameObject);
    	// 建立新的模型对象
    	mCurrentGameObject = Instantiate(models[index]);
    }
    

    最后就是在Unity3D中将工程Build成APK文件,然后再手机或模拟器中运行(如果手机或模拟器连着Eclipse则可以打出log方便调试找错)。

    最后附上代码Demo:

    Unity端代码太大了,所以我就把Android端和Unity端代码还有而apk文件上传到百度云了。

    代码点击下载


    结束语:

    我在刚上大学的时候就梦想着毕业以后去做游戏,但是报志愿的时候选的是java相关,后来才知道java并不适合做游戏,后来自学c++,openGL,再后来又看Unity3D但全都没有坚持下来,而且大学时候天天和同学打wow,结果毕业发现其实游戏相关的东西根本等于一点也没学会,而且大连没有游戏开发的工作,虽然很想去北京试试,但是当时去北京实习太麻烦,而且不一定能找到,所以就在大连直接找了一个Android开发就这么把当初的梦想放弃了。但是我还是想明年去北京试试能不能找到游戏开发的工作,这中间一年我还是会主要学习Android和Java,剩余时间看看图形学和数学知识,不会像大学的时候看到什么热就学什么,因为现在懂得渐渐多了后发现编程基本都是通用的,所以还是先把一个东西学透学明白吧。



    展开全文
  • 2013-10-10 我的脚印

    2014-08-21 12:49:54
    由于参考使用的是wow 60级的美术资源,所以给我造成了一些问题: ... 所幸我有强大无比的模型修改器软件,一一把这些东西添加上后,再使用粒子系统编辑器自己diy了一个飞雪溅出的模型,就有了下面的效果:
    由于参考使用的是wow 60级的美术资源,所以给我造成了一些问题:
    1. 所导出的模型在动画事件帧中没有携带参数,从而无法确认当前是哪只脚落地; 
    2. 所导出的模型包括人物均没有脚部对象插槽,无法确定脚部的即时位置.
     所幸我有强大无比的模型修改器软件,一一把这些东西添加上后,再使用粒子系统编辑器自己diy了一个飞雪溅出的模型,就有了下面的效果:
    展开全文
  • Unity手游之路<七>角色控制器

    千次阅读 2013-12-21 18:44:12
    控制角色模型的移动,同时移动摄影机,改变视角。当然Unity也提供了一些组件,可以让我们做更少的工作,实现我们所期望的功能。今天我们就一起系统来学习相关的内容吧。 (转载请注明原文出处...
  • 本博客分析了一个Tensorflow实现的开源聊天机器人项目deepQA,首先从数据集上和一些重要代码上进行了说明和阐述,最后针对于测试的情况,在deepQA项目上实现了Beam Search的方法,让模型输出的句子更加准确,修改后...
  • 一、伤害判定逻辑与平均伤害  伤害判定逻辑是游戏如何确定...伤害判定逻辑通常采用两种模型,瀑布概率模型与圆桌概率模型。  瀑布模型会按照优先级的顺序对每种情况进行独立判断,而圆桌概率模型则将所有情况放
  • 前几张,我们主要实现了升级经验、人物等级属性、地图、地图怪物,这四种配置的增删查改以及Excel导入功能。我们主要以地图怪物为例,因此在文章末尾提供的源代码中只实现了地图怪物这部分的逻辑功能。  如果你...
  • 计算机网络

    2018-05-15 19:34:23
    1.OSI(open systems interconnection):开放系统互联模型是为了帮助供应商根据协议来构建可互操作的网络设备和软件,以便不同供应商的网络设备能够互相协同工作 2.OSI参考模型 应用层:提供用户接口,特指网络应用程序,...
  • 一.Web知识概述 ... 在互联网交互的过程的有两个非常典型的交互方式——B/S 交互模型(架构)和 C/S 交互模型(架构) C/S架构 Client/Server 客户端/服务器 访问服务器资源必须安装客户端软件...
  • ZZH:魔兽世界之000:MPQ

    千次阅读 2010-03-29 09:14:00
    MPQ MPQ由暴雪的天才程序员Mike OBrien创建的压缩...MPQ应用于许多暴雪出品的游戏,包括WOW。 MPQ格式主要有三部分: Header (plain) Hashtable (encrypted) Datatable (encrypted) libMPQ libMPQ是能读取MPQ的开源库,
  • 搭好了的战斗系统,接下来就需要拆分人物的属性,在成长中逐步投放出去,让玩家有短期长期的追求。 Ø 属性拆分 一个人物的战斗力由很多部分组成: 裸身,宠物,装备,法宝,技能,坐骑等等,属性拆分,就是把...
  • Photoshop读书笔记

    2020-02-25 19:44:18
    *HSB取色步骤一:选择色相值,对应色轮上的度数。 HSB取色步骤二:选择饱和度,可以理解为颜色中有多少白色。...色彩模型+色域=色彩空间 数字图像处理:尺寸更改 记号: C A S Control Windows Ctrl Alt Shift Mac ...
  • Unity3d之角色控制器

    万次阅读 2016-04-10 13:51:27
    控制角色模型的移动,同时移动摄影机,改变视角。当然Unity也提供了一些组件,可以让我们做更少的工作,实现我们所期望的功能。今天我们就一起系统来学习相关的内容吧。 (转载请注明原文出处...
  • Python基础6 面向对象

    2017-08-28 23:00:00
    本节内容 面向对象编程介绍 为什么要面向对象开发? 面向对象的特性:封装,继承,多态 ...假设现在我们需要开发一款简单的游戏,...这时候我们可以写下如下的简单代码来描述这2个角色模型: def person(name,a...
  • flash3d初学者都是从flash或...建模除非简单几何体,任何复杂一点的模型和贴图位置,都只能靠其他三维软件导入,这不光是flash3d的常识,也是游戏制作的基本常识。 甚至很多朋友会以为既然用flash是可以‘画’图形的...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 186
精华内容 74
热门标签
关键字:

wow修改人物模型