obb_obb数据包修改教程 - CSDN
精华内容
参与话题
  • OBB方向包围盒算法实现

    千次阅读 2018-09-28 13:48:36
    如何进行2D旋转矩形的碰撞检测,可以使用一种叫OBB的检测算法(Oriented bounding box)方向包围盒。这个算法是基于SAT(Separating Axis Theorem)分离轴定律的。而OBB不仅仅是计算矩形的碰撞检测,而是一种算法模型。...

    如何进行2D旋转矩形的碰撞检测,可以使用一种叫OBB的检测算法(Oriented bounding box)方向包围盒。这个算法是基于SAT(Separating Axis Theorem)分离轴定律的。而OBB不仅仅是计算矩形的碰撞检测,而是一种算法模型。简单解释一下概念,包围盒和分离轴定律。

    包围盒:是根据物体的集合形状,来决定盒子的大小和方向,这样可以选择最紧凑的盒子来代表物体。见下图

     

    黑色的就是包围盒,可以是凸多边形,最贴近检测物体即可。

     

    分离轴定律:两个凸多边形物体,如果我们能找到一个轴,使得两个在物体在该轴上的投影互不重叠,则这两个物体之间没有碰撞发生,该轴为Separating Axis

    那么用一般去检测那些轴呢,垂直于多边形每条边的轴。如下图:

     

    所以,分离轴定律变成,两个多边形在所有轴上的投影都发生重叠,则判定为碰撞;否则,没有发生碰撞。

     

     

    下面,我只考虑矩形的情况,如何检测分离轴。

    很明显,矩形4条边,有4条检测轴,那么2个矩形就有8个。但是矩形有2个轴是重复的,所以只需要检测2条轴就可以了,既是矩形的两条互相垂直的边所在的轴。

    如上图,判断碰撞,我们需要判断2个矩形在4个轴上的投影是否重叠。这里有2种可能的方式。第一种,把每个矩形的4个顶点投影到一个轴上,这样算出4个顶点最长的连线距离,以后同样对待第二个矩形,最后判断2个矩形投影距离是否重叠。

    第二种方式,把2个矩形的半径距离投影到轴上,以后把2个矩形的中心点连线投影到轴上,以后判断2个矩形的中心连线投影,和2个矩形的半径投影之和的大小。本文使用这种方式。

     

    这里用到一些向量的数学知识。如下图:

     

    P点为矩形在X轴上的投影点,矩形在垂直轴上的投影点为原点。这里也能看出来,点P所在的矩形轴, 在X轴上的投影长度为OP,如果矩形逆时针绕远点O旋转,OP在X轴上的投影长度变小,直到为0,OP垂直于X轴。也就是,OP在X轴上的投影长度的最大与最小值。这也解释了,为什么我们选择检测轴为垂直于多边形边的轴,因为在这些轴上我们能取到极值,中间的那些轴就没必要检测了。

    如何表示轴,我们需要用向量,正确的使用单位向量,能够简化模型,降低思考的难度。如下图:

     

    假设P点的坐标为(px, py), 那么向量P就是(px, py),点P在X轴上的投影点Q坐标是(qx, qy),那么向量Q就是(qx, qy)。我们假设X轴上的单位向量是(1, 0)。那么向量P和X轴上单位向量点乘有:

    向量P * X轴单位向量 = |P| * |X轴单位向量| * cosPQ  = px * 1 + py * 0 = px

    又因为单位向量的长度等于1所以,px就是向量Q的长度。这是非常有意义的,我们就得到一个规律,就是把一个向量点乘一个单位向量,我们得到的是这个向量在这个单位向量上的投影长度。用代码表示为:

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. /** 
    2.  * dot-multiply 
    3.  */  
    4. private float dot(float[] axisA, float[] axisB) {  
    5.     return Math.abs(axisA[0] * axisB[0] + axisA[1] * axisB[1]);  
    6. }  

     

    这里float[] 存放的是一个点的x ,y 坐标。axisB 为单位向量,这个结果就是axisA向量在,单位向量axisB上投影的长度。

     

     

    下面我们看一下,单位向量如何表示:

     

     

    单位向量是用单位圆来描述的。假设这个圆的半径为1,那么圆上的任何一个坐标到原点构成的向量都可以看作一个单位向量,并且长度为1。这般,明显的点P就是一个单位向量。点P在单位圆上移动,那么这个单位向量就在旋转,向量P就和角A建立了关系。很明显的得出,cosA 就是向量P的X坐标,sinA 就是向量P的Y坐标。

    这样我们就可以得出,单位向量P为(cosA,sinA)。这个模型的意义就是把单位向量P可以看成矩形的条边。如下图:

     

     

    那么矩形的另一个边对应的单位向量S如何表示呢,向量S和向量P是垂直的,我们可以得出, S(-sinA, cosA), 向量S 点乘 向量P  = 0

    至此,我们就可以通过一个旋转角度,得到一个矩形的2个检测轴的单位向量。代码如下:

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. // unit vector of x axis  
    2. private float[] axisX;  
    3. // unit vector of y axis  
    4. private float[] axisY;  
    5.    
    6. // 0 -360  
    7. private float rotation;  
    8.    
    9. /** 
    10.  * Set axis x and y by rotation 
    11.  *  
    12.  * @param rotation float 0 - 360  
    13.  */  
    14. public OBB setRotation(float rotation) {  
    15.     this.rotation = rotation;  
    16.    
    17.     this.axisX[0] = MathUtils.cos(rotation);  
    18.     this.axisX[1] = MathUtils.sin(rotation);  
    19.    
    20.     this.axisY[0] = -MathUtils.sin(rotation);  
    21.     this.axisY[1] = MathUtils.cos(rotation);  
    22.    
    23.     return this;  
    24. }  


    下一步如何计算矩形的半径投影呢,什么又是半径投影呢,看下图:

     

    橙色线段,是矩形的2条检测轴,3张图是矩形旋转的3个特殊位置截图。蓝色线段就是矩形半径投影。其实就是,矩形在X轴上最远处的交点,数学上意义就是2条检测轴的投影之和。

    2条检测轴的向量和就是中心点到矩形一个顶点的向量,所以投影半径也是中心点到矩形顶点的向量投影长度。注意向量的方向会影响投影长度。按照中间那幅图,2条检测轴向量和的投影是,2条检测轴投影的差值。如果把其中一个轴,旋转180度,那么2个检测轴和的投影就是,2条轴投影的和值。

     

    至此,如果我们把矩形在任意角度的2条轴向量投影到单位向量上,根据前面的单位向量规律。我们就得到了轴向量在单位向量上投影的长度,而单位向量的长度为1,那么我们得到的就是轴向量与单位向量的比例。在用这个比例乘以轴向量的长度,就得到了轴的投影长度,就能求出轴半径的长度了。如图

     

    2个矩形检测过程中,每次以一个矩形的检测轴为坐标系,投影另一个矩形的检测轴。图中,蓝色线段为左边矩形的半径投影,黄色线段为右边矩形检测轴。我们需要把右边2条检测轴投影到蓝色线段所在X轴的单位向量,得到投影比例,以后在乘以2条检测轴的长度,就可以得到右边矩形的半径投影。

    红色线段为2个矩形的中心点连心,计算其在X轴的投影长度。比较中心点连线的投影长度与2矩形的半径投影长度之和,如果连线投影大,那么在这条轴上没有碰撞,否则碰撞。半径投影代码如下:

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. private float halfWidth;  
    2.    
    3. private float halfHeight;  
    4.    
    5. /** 
    6.  * Get axisX and axisY projection radius distance on axis 
    7.  */  
    8. public float getProjectionRadius(float[] axis) {  
    9.    
    10.     // axis, axisX and axisY are unit vector  
    11.    
    12.     // projected axisX to axis  
    13.     float projectionAxisX = this.dot(axis, this.axisX);  
    14.     // projected axisY to axis  
    15.     float projectionAxisY = this.dot(axis, this.axisY);  
    16.    
    17.     return this.halfWidth * projectionAxisX + this.halfHeight * projectionAxisY;  
    18. }  


    判断2矩形最终是否碰撞,需要依次检测4个分离轴,如果在一个轴上没有碰撞,则2个矩形就没有碰撞。代码如下:

     

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. /** 
    2.  * OBB is collision with other OBB 
    3.  */  
    4. public boolean isCollision(OBB obb) {  
    5.     // two OBB center distance vector  
    6.     float[] centerDistanceVertor = {  
    7.             this.centerPoint[0] - obb.centerPoint[0],  
    8.             this.centerPoint[1] - obb.centerPoint[1]  
    9.     };  
    10.    
    11.     float[][] axes = {  
    12.             this.axisX,  
    13.             this.axisY,  
    14.             obb.axisX,  
    15.             obb.axisY,  
    16.     };  
    17.    
    18.     for(int i = 0; i < axes.length; i++) {  
    19.         // compare OBB1 radius projection add OBB2 radius projection to centerDistance projection  
    20.         if(this.getProjectionRadius(axes[i]) + obb.getProjectionRadius(axes[i])   
    21.                 <= this.dot(centerDistanceVertor, axes[i])) {  
    22.             return false;  
    23.         }  
    24.     }  
    25.    
    26.     return true;  
    27. }  


    最后,给出OBB完整的代码封装

     

     

    Java代码:

     

    [java] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. /** 
    2.  * @author scott.cgi 
    3.  * @since  2012-11-19 
    4.  *   
    5.  * Oriented bounding box  
    6.  */  
    7. public class OBB {  
    8.       
    9.     private float[] centerPoint;  
    10.       
    11.     private float halfWidth;  
    12.       
    13.     private float halfHeight;  
    14.       
    15.     // unit vector of x axis  
    16.     private float[] axisX;  
    17.     // unit vector of y axis  
    18.     private float[] axisY;  
    19.       
    20.     // 0 -360  
    21.     private float rotation;  
    22.       
    23.     private float scaleX;  
    24.     private float scaleY;  
    25.       
    26.       
    27.     private float offsetAxisPointDistance;  
    28.       
    29.       
    30.     /** 
    31.      * Create default OBB 
    32.      *  
    33.      * @param x bornCenterX x 
    34.      * @param y bornCenterY Y 
    35.      * @param halfWidth 
    36.      * @param halfHeight 
    37.      */  
    38.     public OBB(float bornCenterX, float bornCenterY, float halfWidth, float halfHeight) {  
    39.           
    40.         this.halfWidth  = halfWidth;  
    41.         this.halfHeight = halfHeight;  
    42.           
    43.         this.scaleX = 1.0f;  
    44.         this.scaleY = 1.0f;  
    45.           
    46.         this.centerPoint = new float[] {  
    47.                         bornCenterX,  
    48.                         bornCenterY  
    49.         };  
    50.           
    51.         this.axisX = new float[2];  
    52.         this.axisY = new float[2];  
    53.           
    54.         float[] offsetAxisPoint = new float[] {  
    55.                 bornCenterX - Director.getHalfScreenWidth(),  
    56.                 bornCenterY - Director.getHalfScreenHeight()  
    57.         };  
    58.           
    59.         this.offsetAxisPointDistance = (float) Math.sqrt(this.dot(offsetAxisPoint, offsetAxisPoint));  
    60.           
    61.         this.setRotation(0.0f);  
    62.     }  
    63.       
    64.     /** 
    65.      * Create default OBB with born in center screen 
    66.      *  
    67.      * @param halfWidth 
    68.      * @param halfHeight 
    69.      */  
    70.     public OBB(float halfWidth, float halfHeight) {  
    71.         this(Director.getHalfScreenWidth(), Director.getHalfScreenHeight(), halfWidth, halfHeight);  
    72.     }  
    73.       
    74.     /** 
    75.      * Get axisX and axisY projection radius distance on axis 
    76.      */  
    77.     public float getProjectionRadius(float[] axis) {  
    78.           
    79.         // axis, axisX and axisY are unit vector  
    80.           
    81.         // projected axisX to axis  
    82.         float projectionAxisX = this.dot(axis, this.axisX);  
    83.         // projected axisY to axis  
    84.         float projectionAxisY = this.dot(axis, this.axisY);  
    85.           
    86.           
    87.         return this.halfWidth * this.scaleX * projectionAxisX + this.halfHeight * this.scaleY * projectionAxisY;  
    88.     }  
    89.       
    90.     /** 
    91.      * OBB is collision with other OBB 
    92.      */  
    93.     public boolean isCollision(OBB obb) {  
    94.         // two OBB center distance vector  
    95.         float[] centerDistanceVertor = {  
    96.                 this.centerPoint[0] - obb.centerPoint[0],  
    97.                 this.centerPoint[1] - obb.centerPoint[1]  
    98.         };  
    99.           
    100.           
    101.         float[][] axes = {  
    102.                 this.axisX,  
    103.                 this.axisY,  
    104.                 obb.axisX,  
    105.                 obb.axisY,  
    106.         };  
    107.           
    108.         for(int i = 0; i < axes.length; i++) {  
    109.             // compare OBB1 radius projection add OBB2 radius projection to centerDistance projection  
    110.             if(this.getProjectionRadius(axes[i]) + obb.getProjectionRadius(axes[i])   
    111.                     <= this.dot(centerDistanceVertor, axes[i])) {  
    112.                 return false;  
    113.             }  
    114.         }  
    115.           
    116.         return true;  
    117.     }  
    118.       
    119.       
    120.     /** 
    121.      * dot-multiply 
    122.      */  
    123.     private float dot(float[] axisA, float[] axisB) {  
    124.         return Math.abs(axisA[0] * axisB[0] + axisA[1] * axisB[1]);  
    125.     }  
    126.       
    127.     /** 
    128.      * Set axis x and y by rotation 
    129.      *  
    130.      * @param rotation float 0 - 360  
    131.      */  
    132.     public OBB setRotation(float rotation) {  
    133.         this.rotation = rotation;  
    134.           
    135.         this.axisX[0] = MathUtils.cos(rotation);  
    136.         this.axisX[1] = MathUtils.sin(rotation);  
    137.           
    138.         this.axisY[0] = -MathUtils.sin(rotation);  
    139.         this.axisY[1] = MathUtils.cos(rotation);  
    140.           
    141.         this.setCenter(this.centerPoint[0], this.centerPoint[1]);  
    142.   
    143.         return this;  
    144.     }  
    145.       
    146.     /** 
    147.      * Set OBB center point and will add offsetAxis value 
    148.      */  
    149.     public OBB setCenter(float x, float y) {  
    150.         float offsetX = this.offsetAxisPointDistance * MathUtils.cos(this.rotation);  
    151.         float offsetY = this.offsetAxisPointDistance * MathUtils.sin(this.rotation);  
    152.           
    153.         this.centerPoint[0] = x + offsetX;  
    154.         this.centerPoint[1] = y + offsetY;  
    155.           
    156.         return this;  
    157.     }  
    158.       
    159.     /** 
    160.      * Set OBB scale x, y 
    161.      */  
    162.     public OBB setScale(float scaleX, float scaleY) {  
    163.         this.scaleX = scaleX;  
    164.         this.scaleY = scaleY;  
    165.                           
    166.         return this;  
    167.     }  
    168.       
    169.       
    170.     public float getRotation() {  
    171.         return this.rotation;  
    172.     }  
    173.       
    174.     public float getCenterX() {  
    175.         return this.centerPoint[0];  
    176.     }  
    177.       
    178.     public float getCenterY() {  
    179.         return this.centerPoint[1];  
    180.     }  
    181.       
    182.     public float getHalfWidth() {  
    183.         return this.halfWidth * this.scaleX;  
    184.     }  
    185.       
    186.     public float getHalfHeight() {  
    187.         return this.halfHeight * this.scaleY;  
    188.     }  
    189.       
    190. }  


     

     

    c 代码:

     

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. /* 
    2.  * OBBRect.h 
    3.  * 
    4.  *  Created on: 2013-2-11 
    5.  *  Author: scott.cgi 
    6.  */  
    7.   
    8. #ifndef OBBRect_Rect_H_  
    9. #define OBBRect_Rect_H_  
    10.   
    11. #include <stdbool.h>  
    12.   
    13. #include "Mojoc/Graphics/Draw/Rect.h"  
    14. #include "Mojoc/Toolkit/Def/CodeStyle.h"  
    15. #include "Mojoc/Toolkit/Utils/AMathUtils.h"  
    16. #include "Mojoc/Toolkit/Def/StructMember.h"  
    17.   
    18.   
    19. #ifdef __cplusplus  
    20. extern "C" {  
    21. #endif  
    22.   
    23.   
    24. typedef struct OBBRect OBBRect;  
    25.   
    26. /** 
    27.  * Oriented bounding box in Rect shape 
    28.  * Use openGL world coordinate system 
    29.  */  
    30. struct OBBRect {  
    31.   
    32.     float   centerX;  
    33.     float   centerY;  
    34.   
    35.     /** Set origin(0,0) when obbRect create */  
    36.     float   originX;  
    37.     float   originY;  
    38.   
    39.     /** Clockwise [0 - 360] */  
    40.     float   rotation;  
    41.     float   scaleX;  
    42.     float   scaleY;  
    43.   
    44.   
    45.         Get(  
    46.                 /** Unit vector of x axis */  
    47.                 StructMember(Vector2, xAxis);  
    48.   
    49.                 /** Unit vector of y axis */  
    50.                 StructMember(Vector2, yAxis);  
    51.   
    52.                 Rect*   rect;  
    53.   
    54.                 /** Distance of center point to origin(0, 0 */  
    55.                 float   offsetDistance;  
    56.                 /** Degree of vector which center point to origin(0, 0 */  
    57.                 float   offsetDegree;  
    58.   
    59.                 float   halfWidth;  
    60.                 float   halfHeight;  
    61.         );  
    62.   
    63. };  
    64.   
    65.   
    66.   
    67.   
    68. typedef struct {  
    69.         OBBRect* (*create) (Rect* rect);  
    70.         void     (*init)   (Rect* rect, Out(OBBRect* obbRect));  
    71.   
    72.     /** 
    73.      * Set obbRect rotation or origin x,y called updateCenter for 
    74.      * update obbRect center x,y 
    75.      */  
    76.     void (*updateCenter)(OBBRect* obbRect);  
    77.   
    78.   
    79.     /** 
    80.      * Set obbRect rotation 
    81.      */  
    82.     void (*setRotation) (OBBRect* obbRect, float rotation);  
    83.   
    84.   
    85.     /** 
    86.      * OBBRect is collision with other OBBRect 
    87.      */  
    88.     bool (*isCollision) (OBBRect* obbRect, OBBRect* otherObb);  
    89.   
    90.   
    91.     /** 
    92.      * Get real width with scaleX 
    93.      */  
    94.     float (*getWidth)   (OBBRect* obbRect);  
    95.   
    96.   
    97.     /** 
    98.      * Get real height with scaleY 
    99.      */  
    100.     float (*getHeight)  (OBBRect* obbRect);  
    101.   
    102. } _AOBBRect;  
    103.   
    104.   
    105. extern _AOBBRect AOBBRect;  
    106.   
    107.   
    108.   
    109. #ifdef __cplusplus  
    110. }  
    111. #endif  
    112.   
    113. #endif /* OBBRect_H_ */  

     

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片

    1. /* 
    2.  * OBBRect.c 
    3.  * 
    4.  *  Created on: 2013-2-11 
    5.  *  Author: scott.cgi 
    6.  */  
    7.   
    8. #include <malloc.h>  
    9. #include <math.h>  
    10.   
    11. #include "Mojoc/Physics/OBBRect.h"  
    12. #include "Mojoc/Toolkit/Utils/AMathUtils.h"  
    13. #include "Mojoc/Toolkit/Platform/Log.h"  
    14.   
    15.   
    16. static void updateCenter(OBBRect* obbRect) {  
    17.         obbRect->centerX = obbRect->originX +  
    18.                                    obbRect->offsetDistance * obbRect->scaleX *  
    19.                                            AMathUtils_Cos(obbRect->rotation + obbRect->offsetDegree);  
    20.           
    21.         obbRect->centerY = obbRect->originY +  
    22.                                    obbRect->offsetDistance * obbRect->scaleY *  
    23.                                            AMathUtils_Sin(obbRect->rotation + obbRect->offsetDegree);  
    24. }  
    25.   
    26. static void setRotation(OBBRect* obbRect, float rotation) {  
    27.         obbRect->xAxis->x = AMathUtils_Cos(rotation);  
    28.         obbRect->xAxis->y = AMathUtils_Sin(rotation);  
    29.   
    30.         obbRect->yAxis->x = -AMathUtils_Sin(rotation);  
    31.         obbRect->yAxis->y =  AMathUtils_Cos(rotation);  
    32.   
    33.         obbRect->rotation = rotation;  
    34. }  
    35.   
    36. /** 
    37.  * Get axisX and axisY projection radius distance on unit axis 
    38.  */  
    39. static inline float getProjectionRadius(OBBRect* obbRect, Vector2* axis) {  
    40.   
    41.     // axis, axisX and axisY are unit vector  
    42.   
    43.     // projected axisX to axis  
    44.     float projectionAxisX = AMathUtils_DotMultiply2(axis->x, axis->y, obbRect->xAxis->x, obbRect->xAxis->y);  
    45.     // projected axisY to axis  
    46.     float projectionAxisY = AMathUtils_DotMultiply2(axis->x, axis->y, obbRect->yAxis->x, obbRect->yAxis->y);  
    47.   
    48.   
    49.     return obbRect->halfWidth  * obbRect->scaleX * projectionAxisX +  
    50.            obbRect->halfHeight * obbRect->scaleY * projectionAxisY;  
    51.   
    52. }  
    53.   
    54. static bool isCollision(OBBRect* obbRect, OBBRect* otherObb) {  
    55.     // two OBBRect center distance vector  
    56.         Vector2 distanceVec = {  
    57.         obbRect->centerX - otherObb->centerX,  
    58.         obbRect->centerY - otherObb->centerY,  
    59.     };  
    60.   
    61.   
    62.         Vector2* axes[] = {  
    63.                 obbRect->xAxis,  
    64.                 obbRect->yAxis,  
    65.             otherObb->xAxis,  
    66.                 otherObb->yAxis,  
    67.     };  
    68.   
    69.     for (int i = 0; i < 4; i++) {  
    70.         // compare OBBRect1 radius projection add OBBRect2 radius projection to centerDistance projection  
    71.         if (getProjectionRadius(obbRect, axes[i]) + getProjectionRadius(otherObb, axes[i]) <=  
    72.            AMathUtils_DotMultiply2(distanceVec.x, distanceVec.y, axes[i]->x, axes[i]->y)) {  
    73.   
    74.             return false;  
    75.         }  
    76.     }  
    77.   
    78.     return true;  
    79. }  
    80.   
    81. static float getWidth(OBBRect* obbRect) {  
    82.         return (obbRect->halfWidth * 2) * obbRect->scaleX;  
    83. }  
    84.   
    85. static float getHeight(OBBRect* obbRect) {  
    86.         return (obbRect->halfHeight * 2) * obbRect->scaleY;  
    87. }  
    88.   
    89. static inline void initOBBRect(OBBRect* obbRect, Rect* rect) {  
    90.         obbRect->rect           = rect;  
    91.   
    92.         obbRect->halfWidth      = (rect->right   - rect->left) / 2;  
    93.         obbRect->halfHeight     = (rect->bottom  - rect->top)  / 2;  
    94.   
    95.         obbRect->scaleX         = 1.0f;  
    96.         obbRect->scaleY         = 1.0f;  
    97.   
    98.     obbRect->originX        = 0.0f;  
    99.     obbRect->originY        = 0.0f;  
    100.   
    101.         obbRect->centerX        = rect->left + obbRect->halfWidth;  
    102.         obbRect->centerY        = rect->top  - obbRect->halfHeight;  
    103.           
    104.         obbRect->offsetDistance = sqrtf(AMathUtils_DotMultiply2(obbRect->centerX, obbRect->centerY, obbRect->centerX, obbRect->centerY));  
    105.         obbRect->offsetDegree   = AMathUtils_Atan2(obbRect->centerX, obbRect->centerY);  
    106.           
    107.         LogD("centerX = %f, centerY = %f", obbRect->centerX, obbRect->centerY);  
    108.         LogD("offsetDistance = %f, offsetDegree = %f",  
    109.                   obbRect->offsetDistance, obbRect->offsetDegree);  
    110.           
    111.     setRotation(obbRect, 0.0f);  
    112. }  
    113.   
    114. static OBBRect* create(Rect* rect) {  
    115.         OBBRect* obbRect = (OBBRect*) malloc(sizeof(OBBRect));  
    116.         initOBBRect(obbRect, rect);  
    117.   
    118.         return obbRect;  
    119. }  
    120.   
    121. static void init(Rect* rect, Out(OBBRect* obbRect)) {  
    122.         initOBBRect(obbRect, rect);  
    123. }  
    124.   
    125.   
    126. _AOBBRect AOBBRect = {  
    127.         create,  
    128.         init,  
    129.   
    130.         updateCenter,  
    131.         setRotation,  
    132.         isCollision,  
    133.         getWidth,  
    134.         getHeight,  
    135. };  

    本文来自 qing101hua 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qing101hua/article/details/52997160?utm_source=copy

    展开全文
  • obb的生成及使用

    万次阅读 2018-08-03 17:52:51
    obb的制作及使用 obb制作官方文档参考: obb制作及使用官方文档 obb文件生成可参考jobb工具生成(官网推荐) 前言: Google Play应用商店在上传限制100MB大小,超过该大小的应用必须将超过部分以扩展文件的...

    obb的制作及使用

    obb制作官方文档参考:

    前言:

      Google Play应用商店在上传限制100MB大小,超过该大小的应用必须将超过部分以扩展文件的形式进行上传处理,总共可上传2个扩展文件,每个最大文件可为2GB,同时obb文件格式【扩展文件可以使用任何文件格式(ZIP, PDF, MP4, 等)。不管任何文件格式Android都认为他们是obb(opaque binary blobs)文件】可自选。
      对每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件,所以一般只需要处理main扩展文件。

    一 、OBB命名规则

    [main|patch].<expansion-version>.<package-name>.obb

    文件名包含四部分,其中:

    • main|patch: 用来表示是主扩展还是补充扩展。
    • expansion-version: 当前上传apk的version code。
    • package-name: apk的包名
    • obb:后缀,其实本质就是zip文件,Google play会自动将zip文件改为obb文件。

    例如,假设您的APK版本是(versionCode)25,你的包名为com.example.app。如果上传的主扩展文件,该文件被重命名为:

                             main.25.com.example.app.obb

    注意:

    • 上传APK时,OBB会另外上传,不能只更新OBB文件
    • 下载应用程序时,OBB文件下载到%external storage%/Android/obb/package name目录

    二、obb 生成方法:

    1、google官方提供的工具 jobb

      制作成OBB(Opaque Binary Blob)格式文件的一种工具,在Android SDK中, %ANDROID——HOME/tools%中,在tools/bin下有jobb

    命令:

    jobb -d [目录名称的完整路径] -o [输出目标文件的完整路径] -pn [软件包名] -pv [包版本]

    注:如果没有将android sdk配置环境就需要打开命令窗口后将jobb.bat拖入cmd窗口内才能执行

    EG:

    源文件目录 D:contents\main\assets\ 目标文件夹: D:\obb\output.obb 软件包名 com.example.app 包版本 25
    jobb -d D:\contents\ main\assets\ -o D:\obb\output.obb -pn com.example.app -pv 25

    注意:

    • jobb -d,-o 需要指定完整路径。如果指定相对路径,%ANDROID_HOME%/tools/将作为相对路径
    • JOBB工具如果不能输出有一定容量的.obb的文件的话,出现错误提示
    • 下表列出了该jobb工具的命令行选项。
    选项
    描述
    -d 设置用于创建OBB文件的输入目录,或在提取(-dump)现有文件时设置输出目录。创建OBB文件时,指定目录及其所有子目录的内容都包含在OBB文件系统中。
    -o 指定OBB文件的文件名。创建OBB并提取(转储)其内容时,此参数是必需的。
    -pn 指定安装OBB文件的应用程序的软件包名称,该名称对应package于应用程序清单中指定的值。创建OBB文件时需要此参数。
    -pv 设置可以挂载OBB文件的应用程序的最低版本,该版本对应android:versionCode于应用程序清单中的值。创建OBB文件时需要此参数。
    -k 指定用于加密新OBB文件或解密现有加密OBB文件的密码。
    -ov 创建OBB文件,该文件是现有OBB文件结构的叠加层。此选项允许将新包装内容装入与先前包装相同的位置,并用于创建以前生成的OBB文件的修补程序版本。覆盖OBB文件中的文件替换具有相同路径的文件。
    -dump 提取指定OBB文件的内容。使用此选项时,还必须使用-d 参数指定内容的输出目录。
    注意:转储现有OBB文件时,可以省略该 -d 参数以获取文件内的目录列表,而不提取内容。
    -v 设置该工具的详细输出。
    -about 显示该jobb工具的版本和帮助信息。
    2、使用winRAR进行压缩(推荐)

    将需要压缩的文件放入assets 或者根据目录中在使用winRAR进行压缩。
    选中assets文件夹右击—添加到压缩文件,打开下面的窗口,根据命名规则进行命名。

    tip:
    如果你的扩展文件是一些媒体文件并且你不想解压资源包,而是借助media playback call
    (例如MediaPlayer.setDataSource() and SoundPool.load())直接播放资源包里面的媒体文件。
    那么在创建扩展文件包的时候务必不要压缩文件,而仅仅是打包即可 压缩方式为:存储


    这里写图片描述
    压缩过程

    三、扩展文件的保存位置

      当Android Market下载程序的扩展文件的时候会保存到系统的共享存储区。为了确保程序正常运行,您不能删除、移动或者重命名扩展文件。在某些设备上Market无法自动下载该扩展文件,那么您应该在程序启动的时候去下载该文件并且保存到同样的位置。
    扩展文件保存位置如下:

    <shared-storage>/Android/obb/<package-name>/
    <shared-storage> 代表共享文件的目录路径,通过函数getExternalStorageDirectory()获取;
    <package-name> APK的Java包名。
    
    其中shared-storage是设备的primary external storage。

      对于每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件。当更新程序的时候,如果有新的扩展文件则新文件会覆盖旧的扩展文件。
      如果您需要解压缩扩展文件来使用,请注意不要删除该.obb文件,并且也不要把文件解压缩到该目录。您应该把解压缩后的文件保存到getExternalFilesDir()返回的目录下面。如果有可能的话,最好使用程序能直接读取的文件格式而不用再次解压缩文件了。Android开发团队提供了一个项目( APK Expansion Zip Library)可以直接读取ZIP文件中的内容而不用解压缩该文件.
      需要注意的是:保存在系统共享存储区的文件,用户和其他APP也可以访问。

    四、APK扩展文件使用实例

    要在App中使用扩展文件,需要两个附加的Android库项目:

    • Google Market Licensing package-
    • Google Market APK Expansion Library package-

    可以通过Android SDK Manager来下载,也可以直接通过如下链接下载:
    https://dl-ssl.google.com/android/repository/market_licensing-r02.zip
    https://dl-ssl.google.com/android/repository/market_apk_expansion-r01.zip

      下载完成后使用market_licensing-r02.zip文件中的目录google_market_licensing\library来创建一个库项目;
      然后使用market_apk_expansion-r01.zip中的google_market_apk_expansion\downloader_library来创建另外一个库项目。
      同时为了简化对ZIP格式扩展文件的处理,在market_apk_expansion-r01.zip文件中还包含了一个对ZIP文件处理的库项目:google_market_apk_expansion\zip_file。 如果您使用的扩展文件格式是ZIP,那么也可以创建这个库项目。

    1. 声明需要的权限
    <manifest...>
        <!-- Required to access Android Market Licensing -->
        <uses-permissionandroid:name="com.android.vending.CHECK_LICENSE"/>
    
        <!-- Required to download files from Android Market -->
        <uses-permissionandroid:name="android.permission.INTERNET"/>
    
        <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) -->
        <uses-permissionandroid:name="android.permission.WAKE_LOCK"/>
    
        <!-- Required to poll the state of the network connection and respond to changes -->
        <uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/>
    
        <!-- Required to check whether Wi-Fi is enabled -->
        <uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/>
    
        <!-- Required to read and write the expansion files on shared storage -->
        <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        ...
    </manifest>

    注意:默认情况下,下载库项目需要的API level为4 而APK扩展ZIP库项目需要API level为5.
    准备工作完成后,下面来具体看看如何使用扩展文件。

    2. 实现下载服务(Implementing the downloader service)

      为了实现在后台下载文件,下载库项目提供了一个Service实现,名称为DownloaderService。您应该继承自这个文件来实现您的下载服务。为了简化下载服务的开发,该DownloaderService还实现了如下功能:

    • 注册一个BroadcastReceiver来监听设备的网络连接状态的改变。如果网络连接断开就暂停下载;如果网络连接恢复就继续下载。-
    • 安排一个 RTC_WAKEUP 通知,当下载服务被终结的时候可以通过该通知来启动下载服务
    • 生成一个通知(Notification )来显示下载的进度以及下载错误等状态
    • 允许您的程序手工的暂停和恢复下载
    • 检测共享存储区挂载了并且可用,在下载文件之前检测 文件是否已经存在、存储空间是否足够。如果出现问题就通知用户。

        您仅仅需要创建一个继承自DownloaderService的类,并且实现如下三个函数即可:
      getPublicKey():您Market账号的 Base64 编码 RSA 公共密钥,可以通过如下网址获取:
      https://market.android.com/publish/Home#ProfileEditorPlace:

    getSALT(): 许可策略用来生成混淆器(Obfuscator)的一组随机bytes。

    getAlarmReceiverClassName(): 返回您程序中用来重启下载进程的BroadcastReceiver类名称。当某些情况下,下载服务被意外终止的时候通过该BroadcastReceiver类来重新下载。比如 进程管理的程序终止了下载服务。
      

    • 下面是一个DownloaderService类的实现代码:
    public class SampleDownloaderService extends DownloaderService {
        // You must use the public key belonging to your publisher account
        public static final String BASE64_PUBLIC_KEY ="YourAndroidMarketLVLKey";
        // You should also modify this salt
        public static final byte[] SALT =new byte[] {1,42, -12, -1,54,98,
                -100, -12,43,2, -8, -4,9,5, -106, -107, -33,45, -1,84
        };
    
        @Override
        public String getPublicKey() {
            return BASE64_PUBLIC_KEY;
        }
    
        @Override
        public byte[] getSALT() {
            return SALT;
        }
    
        @Override
        public String getAlarmReceiverClassName() {
            return SampleAlarmReceiver.class.getName();
        }
    }

      然后 在Manifest文件中声明该Service即可。非常简单吧!

    <application ...>
        <service android:name=".SampleDownloaderService" />    
    </application>
    • 实现AlarmReceiver
        为了检测下载进程和重启下载服务,DownloaderService会安排一个RTC_WAKEUP Alarm来发送一个Intent到程序的 BroadcastReceiver。你必需定义这个 BroadcastReceiver 来调用 Downloader Library提供的函数,通过该函数来检测下载状态和在必要的情况下重启下载服务。
        实现这个类也是非常简单的,一般来说只要覆写onReceive()函数并且调用DownloaderClientMarshaller.startDownloadServiceIfRequired()函数即可。
      定义AlarmReceiver继承自BroadcastReceiver如下所示:
    public class AlarmReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            try {
                PrintLog.d("AlarmReceiver startDownloadServiceIfRequired");
                DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,
                 YRDownloaderService.class);
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }       
        }
    
    }

      然后 在Manifest文件中声明该Receiver即可。非常简单吧!

    <application ...>
        <service android:name=".AlarmReceiver" />    
    </application>
    3.开始下载扩展文件

    程序的主Activity(通过Launcher图标启动的Activity)应该负责检查扩展文件是否存在、如果不存在就启动下载服务。
    使用Downloader Library来下载需要遵守如下步骤:

    1)检查文件是否已经下载了
    Downloader Library中的Helper类中包含了一些函数来简化这个步骤:
    getExtendedAPKFileName(Context, c, boolean mainFile, int versionCode)
    doesFileExist(Context c, String fileName, long fileSize)
    例如在示例项目中,在Activity的onCreate()函数中通过如下函数来检查文件是否存在:

    boolean expansionFilesDelivered() {
        for(XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, xf.mFileVersion);
            if(!Helpers.doesFileExist(this, fileName, xf.mFileSize,false))
                returnfalse;
        }
        returntrue;
    }

    这里的XAPKFile对象保存了已知扩展文件的版本号和大小以及是否为main扩展文件。如果该函数返回false则启动下载服务。

    2)通过 DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, ClassserviceClass)该函数来开始下载。
    该函数的参数如下:

    • context: Your application’s Context.
    • notificationClient: 用来启动主Activity的PendingIntent。用在DownloaderService 创建的用来显示下载进度的通知中。当用户选择该通知,系统调用该PendingIntent来打开显示下载进度的Activity(一般而言就是启动下载的Activity)。
    • serviceClass: 程序中继承自DownloaderService的类。在必要的情况下会启动该服务来开始下载。

    这个函数返回一个整数来表示是否有必要下载文件。有如下几个值:

    • NO_DOWNLOAD_REQUIRED: 表示文件已经存在或者当前正在下载。
    • LVL_CHECK_REQUIRED:表示需要授权验证来获取下载扩展文件的URL。
    • DOWNLOAD_REQUIRED: 表示扩展文件的URL已经获取到了,但是还没开始下载。
    • LVL_CHECK_REQUIRED 和 DOWNLOAD_REQUIRED 在本质上是一样的,一般而言您无需关注这个状态。在您的主Activity中调用 startDownloadServiceIfRequired(),你只需要看看返回值是否为NO_DOWNLOAD_REQUIRED即可。如果返回值不是NO_DOWNLOAD_REQUIRED, Downloader Library 开始启动下载,您应该更新程序界面来显示下载进度;如果返回值是 NO_DOWNLOAD_REQUIRED,表明该文件已经下载好了,您的程序可以正常启动了。

    例如:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Check if expansion files are available before going any further
        if(!expansionFilesDelivered()) {
            // Build an Intent to start this activity from the Notification
            Intent notifierIntent =newIntent(this, MainActivity.getClass());
            notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            ...
            PendingIntent pendingIntent = PendingIntent.getActivity(this,0,
                    notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Start the download service (if required)
            intstartResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize this activity to show download progress
            if(startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download progress (next step)
                ...
                return;
            }// If the download wasn't necessary, fall through to start the app
        }
        startApp();// Expansion files are available, start the app
    }

    3)  当 startDownloadServiceIfRequired() 函数的返回值不是NO_DOWNLOAD_REQUIRED的时候,
    调用DownloaderClientMarshaller.CreateStub(IDownloaderClient client, ClassdownloaderService)函数来创建一个IStub实例。这个IStub实例提供了Activity和下载服务之前的绑定功能,这样您的Activity就可以收到下载事件了。
      CreateStub()函数需要一个实现了IDownloaderClient接口的类和DownloaderService的实现类作为参数。一般而言只要让Activity实现IDownloaderClient接口即可。
      Android开发团队推荐在Activity的onCreate()函数中创建IStub对象(在startDownloadServiceIfRequired()函数之后创建)。

    例如:

    // Start the download service (if required)
    int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                    pendingIntent, SampleDownloaderService.class);
    // If download has started, initialize activity to show progress
    if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
        // Instantiate a member instance of IStub
        mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                SampleDownloaderService.class);
        // Inflate layout that shows download progress
        setContentView(R.layout.downloader_ui);
        return;
    }

      当onCreate()函数返回以后,Activity会执行onResume()函数,在该函数中调用IStub的 connect() 函数。同样在onStop()函数中调用IStub的 disconnect()函数。

    例如:

    @Override
    protectedvoidonResume() {
        if(null!= mDownloaderClientStub) {
            mDownloaderClientStub.connect(this);
        }
        super.onResume();
    }
    
    @Override
    protectedvoidonStop() {
        if(null!= mDownloaderClientStub) {
            mDownloaderClientStub.disconnect(this);
        }
        super.onStop();
    }

    调用connect()用来绑定Activity和DownloaderService 。

    4. 处理下载进度

      要接收下载进度信息,需要实现IDownloaderClient 接口。该接口有如下函数:

    onServiceConnected(Messenger m)
      在初始化完IStub后,会回调该函数。该函数的参数是用来访问您的DownloaderService的,通过 DownloaderServiceMarshaller.CreateProxy()函数来创建这个IDownloaderService对象,然后可以用这个对象来控制下载服务,比如 暂停、继续下载等。

    推荐的实现方式:

    private IDownloaderService mRemoteService;
    ...
    
    @Override
    public void onServiceConnected(Messenger m) {
        mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
        mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
    }

    onDownloadStateChanged(int newState)
      当下载状态发生变化的时候调用该函数,例如 开始下载或者下载完成。

      参数newState的值是IDownloaderClient接口中定义的一些常量之一(以 STATE_ 开头的);
      可以通过函数 Helpers.getDownloaderStringResourceIDFromState()来获取一个状态的文本描述,这样用户更容易理解。例如 STATE_PAUSED_ROAMING 对应的文本描述是: “Download paused because you are roaming/当前在漫游状态,下载停止”

    onDownloadProgress(DownloadProgressInfo progress)
      该函数的参数DownloadProgressInfo包含了下载进度的各种信息,例如 预计完成时间、当前下载速度、完成的百分比等。可以根据该信息来更新下载界面。

    另外还有一些有用的函数:

    • requestPauseDownload()
      暂停下载
    • requestContinueDownload()
      恢复下载
    • setDownloadFlags(int flags)
      设置下载的网络标示。当前只支持一个标示:FLAGS_DOWNLOAD_OVER_CELLULAR。 通过移动网络下载扩展文件。默认情况下该标示没有启用,所以默认情况下只通过WIFI下载。
    5.读取扩展文件

      将APK扩展文件下载下来之后,紧接着我们就要考虑如何使用它了。但是Android 6.0的一些实现(API级别23)以及以后的一些实现仍然需要权限,因此需要在应用程序清单中声明存储权限,并在运行时请求外部存储权限,如下所示:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    或者
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

      在使用obb扩展文件前需要将Zip Library的库移入自己的项目中进行依赖使用此库可以轻松地将ZIP扩展文件中的资源作为虚拟文件系统读取。

    Tip:(Zip Library位于其中/extras/google/google_market_apk_expansion/zip_file/)

      使用APKExpansionSupport类来获取obb中的资源
      提供一些访问扩展文件名和ZIP文件的方法:

      getAPKExpansionFiles()
    上面显示的相同方法返回两个扩展文件的完整文件路径。
    getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
    返回ZipResourceFile表示主文件和补丁文件的总和。也就是说,如果同时指定mainVersion和patchVersion,则返回一个ZipResourceFile提供对所有数据的读访问权,并将补丁文件的数据合并到主文件的顶部。
      ZipResourceFile
    表示共享存储上的ZIP文件,并执行基于ZIP文件提供虚拟文件系统的所有工作。您可以使用- APKExpansionSupport.getAPKExpansionZipFile()或ZipResourceFile通过将实例传递给扩展文件来获取实例。此类包含各种有用的方法,但您通常不需要访问其中的大多数方法。一些重要的方法是:
    • getInputStream(String assetPath)
      提供InputStream读取ZIP文件中的文件。的 assetPath必须的路径所需的文件,相对于的ZIP文件内容的根。
    • getAssetFileDescriptor(String assetPath)
      提供一个AssetFileDescriptor对ZIP文件中的文件。的assetPath必须的路径所需的文件,相对于的ZIP文件内容的根。这对某些需要的Android API很有用AssetFileDescriptor

    首先要获取扩展文件的路径,可以通过如下代码完成该操作:

    // The shared path to all app expansion files
    private final static String EXP_PATH ="/Android/obb/";
    
    static String[] getAPKExpansionFiles(Context ctx, intmainVersion,intpatchVersion) {
        String packageName = ctx.getPackageName();
        Vector<String> ret =newVector<String>();
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            // Build the full path to the app's expansion files
            File root = Environment.getExternalStorageDirectory();
            File expPath =newFile(root.toString() + EXP_PATH + packageName);
    
            // Check that expansion file path exists
            if(expPath.exists()) {
                if( mainVersion >0) {
                    String strMainPath = expPath + File.separator +"main."+
                            mainVersion +"."+ packageName +".obb";
                    File main =newFile(strMainPath);
                    if( main.isFile() ) {
                            ret.add(strMainPath);
                    }
                }
                if( patchVersion >0) {
                    String strPatchPath = expPath + File.separator +"patch."+
                            mainVersion +"."+ packageName +".obb";
                    File main =newFile(strPatchPath);
                    if( main.isFile() ) {
                            ret.add(strPatchPath);
                    }
                }
            }
        }
        String[] retArray =newString[ret.size()];
        ret.toArray(retArray);
        returnretArray;
    }
    示例提取资源:
    ProvinceUtil    provinceUtil = new ProvinceUtil();
    ZipResourceFile expansionFile = null;
    try {
        expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getApplicationContext(), BuildConfig.VERSION_CODE, 0);
        if (expansionFile != null) {
            AssetFileDescriptor fd = 
            expansionFile.getAssetFileDescriptor("assets/my_province_data.txt");
            PrintLog.e("-------" + fd.getLength());
            InputStream inputStream = expansionFile.getInputStream("assets/my_province_data.txt");
            if (inputStream != null) {
                provinceUtil.initProvinceDatas(inputStream);
                String[] provinces = provinceUtil.getmProvinceDatas();
                if (provinces != null) {
                    //参考ArrayAdapter的构造函数
                    listView.setAdapter(new ArrayAdapter<String>(this,
                            android.R.layout.simple_list_item_1,
                            provinces));
                }
            }
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    
    
    展开全文
  • Android obb解压

    千次阅读 2019-01-25 14:48:02
    // obb  public static String getObbFilePath(Context context) {  try {  Log.d("bbt", "getObbFilePath : packetName=" + context.getPackageName());  Log.d("bbt", "...

        // obb
        public static String getObbFilePath(Context context) {
            try {
                Log.d("bbt", "getObbFilePath : packetName=" + context.getPackageName());
                Log.d("bbt", "getObbFilePath : absolutePath=" + Environment.getExternalStorageDirectory().getAbsolutePath());
                Log.d("bbt", "getObbFilePath : versionCode=" + context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode);
                return Environment.getExternalStorageDirectory().getAbsolutePath()
                        + "/Android/obb/"
                        + context.getPackageName()
                        + File.separator
                        + "main."
                        + context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode
                        + "."
                        + context.getPackageName()
                        + ".obb";
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        public static void createDirectoryIfNeeded(String folderPath) {
            File folder = new File(folderPath);
            if (!folder.exists() || !folder.isDirectory()) {
                folder.mkdirs();
            }
        }

        public static void unZip(File zipFile, String outPathString) {
            Log.d("bbt", "unZip " + zipFile.getName() + " to " + outPathString);
            try {
                createDirectoryIfNeeded(outPathString);
                ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFile));
                ZipEntry zipEntry;
                String szName;
                while ((zipEntry = inZip.getNextEntry()) != null) {
                    szName = zipEntry.getName();
                    if (zipEntry.isDirectory()) {
                        szName = szName.substring(0, szName.length() - 1);
                        File folder = new File(outPathString + File.separator + szName);
                        folder.mkdirs();
                    } else {
                        File file = new File(outPathString + File.separator + szName);
                        createDirectoryIfNeeded(file.getParent());
                        file.createNewFile();
                        FileOutputStream out = new FileOutputStream(file);
                        int len;
                        byte[] buffer = new byte[1024];
                        while ((len = inZip.read(buffer)) != -1) {
                            out.write(buffer, 0, len);
                            out.flush();
                        }
                        out.close();
                    }
                }
                inZip.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public static void unZipObb(String srcFilePath, String dstFolderPath) {
            Log.d("bbt", "unZipObb : srcFilePath=" + srcFilePath + ", dstFolderPath="+dstFolderPath);
            Context context = m_pActivity;
            if (context == null) {
                Log.d("bbt", "unZipObb error : context == null");
                return;
            }

            try {
                String obbFilePath = srcFilePath;
                if (obbFilePath.equals("")) {
                    obbFilePath = getObbFilePath(context);
                }
                if (obbFilePath == null) {
                    Log.d("bbt", "unZipObb error : obbFilePath == null");
                    return;
                } else {
                    File obbFile = new File(obbFilePath);
                    if (!obbFile.exists()) {
                        //下载obb文件
                        Log.d("bbt", "unZipObb error : !obbFile.exists()");
                    } else {
                        File outputFolder = new File(dstFolderPath);
                        if (!outputFolder.exists()) {
                            //目录未创建 没有解压过
                            outputFolder.mkdirs();
                            unZip(obbFile, outputFolder.getAbsolutePath());
                        } else {
                            //目录已创建 判断是否解压过
                            if (outputFolder.listFiles() == null) {
                                //解压过的文件被删除
                                unZip(obbFile, outputFolder.getAbsolutePath());
                            } else {
                                //此处可添加文件对比逻辑
                                Log.d("bbt", "unZipObb error : outputFolder.listFiles() != null");
                            }
                        }
                    }
                }
            } catch (Exception e) {
                Log.d("bbt", "unZipObb error : Exception");
                e.printStackTrace();
            }

    展开全文
  • 碰撞检测之OBB-OBB检测

    万次阅读 2016-03-07 02:20:14
    2D情况首先回顾一下SAP两个凸包多边形,当且仅当存在一条线,这两个多边形在这条线上的投影不相交,则这两个多边形也不相交.这条线称为Separating Axis.垂直Separating Axis存在一条Separating Line将两个多边形分开...

    2D情况

    首先回顾一下SAP



    两个凸包多边形,当且仅当存在一条线,这两个多边形在这条线上的投影不相交,则这两个多边形也不相交.


    这条线称为Separating Axis.垂直Separating Axis存在一条Separating Line将两个多边形分开。


    这里还有一个要确定的,就是如果两个矩形之间存在Separating Line,则一定存在一条和两个矩形中的一条边平行。每个矩形对边平行,则我们只需要检查四个方向是否存在Separating Line,如下图



    找Separating Line 就是找Separating Axis, Separating Axis也只有四个方向,所以将矩形在四个轴上投影就可以了,如下图









    下面来看具体的计算


    定义下面几个变量

    PA = coordinate position of the center of rectangle A
    Ax = unit vector representing the local x-axis of A
    Ay = unit vector representing the local y-axis of A
    WA = half width of A (corresponds with the local x-axis of A)
    HA = half height of A (corresponds with the local y-axis of A)


    PB = coordinate position of the center of rectangle B

    Bx = unit vector representing the local x-axis of B
    By = unit vector representing the local y-axis of B
    WB = half width of B (corresponds with the local x-axis of B)
    HB = half height of B (corresponds with the local y-axis of B)


    T= PB - PA

    轴L是Seprating Axis的条件是

    |Proj ( T )| > 0.5 * |Proj ( RectangleA )| + 0.5 *|Proj ( RectangleB )|


    Proj是投影计算, 展开

    | T • L | > | ( WA*Ax ) • L | + | ( HA*Ay ) • L | + | ( WB*Bx ) • L | + |( HB*By ) • L |

    L只有四种情况,AX, AY, BX, BY

    CASE 1:
    // L = Ax
    | T • Ax | > | ( WA*Ax ) • Ax | + | ( HA*Ay ) • Ax | + | ( WB*Bx ) • Ax | + |( HB*By ) • Ax |
    | T • Ax | > WA + 0 + | ( WB*Bx ) • Ax | + |( HB*By ) • Ax |
    | T • Ax | > WA + | ( WB*Bx ) • Ax | + |( HB*By ) • Ax |

    如果成立,存在Separating Axis平行Ax。


    CASE 2:
    // L = Ay
    | T • Ay | > HA + | ( WB*Bx ) • Ay | + |( HB*By ) • Ay |

    如果成立,存在Separating Axis平行Ay。


    CASE 3:
    // L = Bx
    | T • Bx | > | ( WA* Ax ) • Bx | + | ( HA*Ay ) • Bx | + WB

    如果成立,存在Separating Axis平行Bx。


    CASE 4:
    // L = By
    | T • By | > | ( WA* Ax ) • By | + | ( HA*Ay ) • By | + HB

    如果成立,存在Separating Axis平行By。



    三维情况

    在三维情况下的,之前的Separating Line就变成了Separating Plane,如下图



    每个Box都有三组面,每组面都是平行的,Separating Plane都是平行其中的一个面。则两个box的SAT中可能的Separating Axis有六个




    下图中Separating Plane就平行于右边Box的一个面



    然而还有一种情况,如下



    这种情况,两个Box并不相交,但是他们并没有发生碰撞,这种情况,Separating Plane的法线是两条红线的叉乘



    所以在三维情况下Box和Box的碰撞检测需要判定的情况有6+9种

    CASE 1:L = Ax  CASE 2:L = Ay  CASE 3:L = Az   CASE 4:L = Bx   CASE 5:L = By  CASE 6:L = Bz

    CASE 7:L = Ax Bx  CASE 8:L = Ax By  CASE 9:L = Ax Bz  CASE 10:L = Ay Bx  

    CASE 11:L = Ay By  CASE 12:L = Ay Bz  CASE 13:L = Az Bx  CASE 14:L = Az By  CASE 15:L = Az Bz


    判断条件还是

    | T • L | > | ( WA*Ax ) • L | + | ( HA*Ay ) • L | + |( DA*Az ) • L |+ | ( WB*Bx ) • L | + |( HB*By ) • L | + |( DB*Bz ) • L |

    一点优化,关于T • L 

    T • (Ax * Bx) =(T •  Az)(Ay •  Bx) - (T•  Ay)(Az •  Bx)

    这里将有叉乘的地方进行了转化,要看证明的请看参考资料。


    好,可以上代码了

      public static bool IntersectBoxBox(Box box0, Box box1)
            {
                Vector3 v = box1.center - box0.center;
    
                //Compute A's basis
                Vector3 VAx = box0.rotation * new Vector3(1, 0, 0);
                Vector3 VAy = box0.rotation * new Vector3(0, 1, 0);
                Vector3 VAz = box0.rotation * new Vector3(0, 0, 1);
    
                Vector3[] VA = new Vector3[3];
                VA[0] = VAx;
                VA[1] = VAy;
                VA[2] = VAz;
    
                //Compute B's basis
                Vector3 VBx = box1.rotation * new Vector3(1, 0, 0);
                Vector3 VBy = box1.rotation * new Vector3(0, 1, 0);
                Vector3 VBz = box1.rotation * new Vector3(0, 0, 1);
    
                Vector3[] VB = new Vector3[3];
                VB[0] = VBx;
                VB[1] = VBy;
                VB[2] = VBz;
    
                Vector3 T = new Vector3(Vector3.Dot(v, VAx), Vector3.Dot(v, VAy), Vector3.Dot(v, VAz));
    
                float[,] R = new float[3, 3];
                float[,] FR = new float[3, 3];
                float ra, rb, t;
    
                for (int i = 0; i < 3; i++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        R[i, k] = Vector3.Dot(VA[i], VB[k]);
                        FR[i, k] = 1e-6f + Mathf.Abs(R[i, k]);
                    }
                }
    
                // A's basis vectors
                for (int i = 0; i < 3; i++)
                {
                    ra = box0.extents[i];
                    rb = box1.extents[0] * FR[i, 0] + box1.extents[1] * FR[i, 1] + box1.extents[2] * FR[i, 2];
                    t = Mathf.Abs(T[i]);
                    if (t > ra + rb) return false;
                }
    
                // B's basis vectors
                for (int k = 0; k < 3; k++)
                {
                    ra = box0.extents[0] * FR[0, k] + box0.extents[1] * FR[1, k] + box0.extents[2] * FR[2, k];
                    rb = box1.extents[k];
                    t = Mathf.Abs(T[0] * R[0, k] + T[1] * R[1, k] + T[2] * R[2, k]);
                    if (t > ra + rb) return false;
                }
    
                //9 cross products
    
                //L = A0 x B0
                ra = box0.extents[1] * FR[2, 0] + box0.extents[2] * FR[1, 0];
                rb = box1.extents[1] * FR[0, 2] + box1.extents[2] * FR[0, 1];
                t = Mathf.Abs(T[2] * R[1, 0] - T[1] * R[2, 0]);
                if (t > ra + rb) return false;
    
                //L = A0 x B1
                ra = box0.extents[1] * FR[2, 1] + box0.extents[2] * FR[1, 1];
                rb = box1.extents[0] * FR[0, 2] + box1.extents[2] * FR[0, 0];
                t = Mathf.Abs(T[2] * R[1, 1] - T[1] * R[2, 1]);
                if (t > ra + rb) return false;
    
                //L = A0 x B2
                ra = box0.extents[1] * FR[2, 2] + box0.extents[2] * FR[1, 2];
                rb = box1.extents[0] * FR[0, 1] + box1.extents[1] * FR[0, 0];
                t = Mathf.Abs(T[2] * R[1, 2] - T[1] * R[2, 2]);
                if (t > ra + rb) return false;
    
                //L = A1 x B0
                ra = box0.extents[0] * FR[2, 0] + box0.extents[2] * FR[0, 0];
                rb = box1.extents[1] * FR[1, 2] + box1.extents[2] * FR[1, 1];
                t = Mathf.Abs(T[0] * R[2, 0] - T[2] * R[0, 0]);
                if (t > ra + rb) return false;
    
                //L = A1 x B1
                ra = box0.extents[0] * FR[2, 1] + box0.extents[2] * FR[0, 1];
                rb = box1.extents[0] * FR[1, 2] + box1.extents[2] * FR[1, 0];
                t = Mathf.Abs(T[0] * R[2, 1] - T[2] * R[0, 1]);
                if (t > ra + rb) return false;
    
                //L = A1 x B2
                ra = box0.extents[0] * FR[2, 2] + box0.extents[2] * FR[0, 2];
                rb = box1.extents[0] * FR[1, 1] + box1.extents[1] * FR[1, 0];
                t = Mathf.Abs(T[0] * R[2, 2] - T[2] * R[0, 2]);
                if (t > ra + rb) return false;
    
                //L = A2 x B0
                ra = box0.extents[0] * FR[1, 0] + box0.extents[1] * FR[0, 0];
                rb = box1.extents[1] * FR[2, 2] + box1.extents[2] * FR[2, 1];
                t = Mathf.Abs(T[1] * R[0, 0] - T[0] * R[1, 0]);
                if (t > ra + rb) return false;
    
                //L = A2 x B1
                ra = box0.extents[0] * FR[1, 1] + box0.extents[1] * FR[0, 1];
                rb = box1.extents[0] * FR[2, 2] + box1.extents[2] * FR[2, 0];
                t = Mathf.Abs(T[1] * R[0, 1] - T[0] * R[1, 1]);
                if (t > ra + rb) return false;
    
                //L = A2 x B2
                ra = box0.extents[0] * FR[1, 2] + box0.extents[1] * FR[0, 2];
                rb = box1.extents[0] * FR[2, 1] + box1.extents[1] * FR[2, 0];
                t = Mathf.Abs(T[1] * R[0, 2] - T[0] * R[1, 2]);
                if (t > ra + rb) return false;
    
                return true;
            }

    测试代码

    public class BoxBoxTester : MonoBehaviour {
        public GameObject box;
        public GameObject box1;
        Box _box;
        Box _box1;
        // Use this for initialization
        void Start()
        {
            _box = new Box();
            _box1 = new Box();
        }
    
        // Update is called once per frame
        void Update()
        {
            _box.center = box.transform.position;
            _box.rotation = box.transform.rotation;
            _box.extents = 0.5f * box.transform.localScale;
    
            _box1.center = box1.transform.position;
            _box1.rotation = box1.transform.rotation;
            _box1.extents = 0.5f * box1.transform.localScale;
    
            if (NIntersectTests.IntersectBoxBox(_box, _box1))
            {
                box.GetComponent<MeshRenderer>().materials[0].SetColor("_Color", new Color(1, 0, 0));
            }
            else
            {
                box.GetComponent<MeshRenderer>().materials[0].SetColor("_Color", new Color(1, 1, 1));
            }
        }
    }
    


    运行结果




    参考

    Separating Axis Theorem for Oriented Bounding Boxes

    展开全文
  • Unity中安卓APP安装OBB分包(基础APK+OBB)

    千次阅读 2018-08-01 16:16:24
    Unity中安卓APP安装OBB分包(基础APK+OBB) 1、首先在Unity打好分包,出分包的方法如下图所示 准备好分包打包的Android安装包(apk+obb),比如说是:ABC.apk + ABC.obb   2、先把apk安装到Android设备,然后将...
  • 算法与游戏之OBB碰撞算法

    千次阅读 2017-01-17 10:28:45
    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。...
  • APK和obb的方法

    千次阅读 2019-07-09 16:39:07
    准备好分包打包的Android安装包(apk+obb),比如说是:ABC.apk + ABC.obb   2、先把apk安装到Android设备,然后将对应obb文件改名为: main.<Bundle Version Code>.<包名>.obb 并拷贝到Android...
  • 注:文章来自于我的博客shawnluo.com,... 首先,讲一个题外话:手机如何连接U盘 外部设备:OTG数据线,U盘 内部准备:打开手机开发者模式 ...?...→打开OTG→打开开发者选项→打开USB调试 最后:将USB接上U盘,插...
  • OBB文件超出2GB的错误 Unreal

    千次阅读 2018-08-20 17:12:13
    AndroidPlatform.Package: OBB exceeds 2 GiB limit: 2688018369 bytes &lt;&gt;c.&lt;PrintExceptionInfo&gt;b__4_1: =========================================================================...
  • OBB包围盒

    千次阅读 2017-05-15 20:46:25
    前面提到了长条物体在旋转时AABB盒的变化,那么是否有能够在任意方向都更为精确的检测方式,答案是肯定的,这是一种基于OBB即定向包容盒子(Oriented Bounding Box,OBB)的技术,它已经广泛用于光线追踪和碰撞检测中...
  • AABB&OBB碰撞

    千次阅读 2016-12-27 11:50:43
    包围体是一个简单的几何空间,里面包含着复杂形状的物体。为物体添加包围体的目的是快速的进行碰撞检测或者进行精确...包围体类型包括球体、轴对齐包围盒(AABB)、有向包围盒(OBB)、8-DOP以及凸壳。如图1所示。
  • 二维中的OBB相交测试

    2018-05-11 22:06:46
    二维中的OBB相交测试置顶2013年10月23日 21:05:53阅读数:2087 1. 背景知识OBB全称oriented bounding box,比AABB(axis-aligned bounding box)多了一个方向性。求交核心思想:向量点积的投影意义,unitX为(1,0)...
  • 使用OBB来进行碰撞检测,避免使用传统AABB进行碰撞检测的不精确性。
  • 矩形旋转碰撞,OBB方向包围盒算法实现
  • obb downloader plugin

    热门讨论 2015-04-20 14:41:41
    obb downloader plugin
  • Cocos Creator实现Google Play obb 分包

    千次阅读 2019-10-26 11:21:41
    Google Play 提供了 obb 分包方案,来解决包体问题。 OBB 是 Opaque Binary Blob 的缩写,是一种类型 zip 文件格式,作为安卓应用的扩展数据包。 参考:安卓开发指南和百度百科 思路 游戏多数资源都不需要在启动游戏...
  • unity android obb

    2016-04-28 10:24:04
    unity android obb
  • 安卓obb文件的使用进阶

    千次阅读 2020-03-16 14:50:28
          谷歌后台关于obb文件使用,有2个比较重要的设定    ...1、可以上传新版本的apk,指定使用旧版本的...2、可以使用patch.obb文件,对正式的obb文件打补丁。  ...
  • 用unity 导出OBB包之后把OBB包放到 安卓路径/obb/包名的目录里面 然而将APK包安装好之后却无法加载OBB包里的东西。且报错。![图片说明](https://img-ask.csdn.net/upload/201509/29/1443511856_58392.png)但是在出包...
  • OBB盒的实现

    2019-09-25 14:00:28
    制造几何仿真中的碰撞检测通常视为针对刚体对象间的碰撞检测,这样的话可以把非刚体即软体的建模和变形算法对碰撞检测的影响减少到最小。常见成熟的基于包围盒的碰撞检测(box intersection test)算法如: ...
1 2 3 4 5 ... 20
收藏数 3,770
精华内容 1,508
关键字:

obb