2015-06-02 12:13:27 qq15233635728 阅读数 9780

利用Unity3d制作完毕游戏发布到appstore,有时会做游戏内购买虚拟物品,也就是内购。

在Ios开发中叫做:In App Purchase,简称IAP

那么如何在unity3d内嵌入IPA呢?几经辗转,多方搜索,摸索出点经验,分享给大家,如有疏漏,还请指教。

当然也有人们写好的插件可用,我觉得自己写的才用着顺手。

 

一、准备条件:

1、 申请苹果开发者账号。后台先创建证书,在创建应用,填写应用详情,创建测试用的账号,创建内购项目。

这里根据需要创建consumable(每次都需要购买)或者non-consumable的(购买一次一直可用,就是如果买过可以恢复购买)内购项目。

如下我们创建了non-consumable类型,名称“Package_2”,这个名称仅能使用一次,即使删除了也不能够再次利用。


 

创建测试人员账号,可以免费测试购买本开发者账号下面所有应用的物品:


 

2、简单了解ios开发的Object-C语言,主要是用来做内购,详细请百度:

3、简单了解IPA

详细请参见:StoreKit Guide(In App Purchase)翻译

http://yarin.blog.51cto.com/1130898/549141

4、了解unity3d与ios通信,详细参见:为iOS创建插件 Building Plugins for iOS

http://game.ceeger.com/Manual/PluginsForIOS.html

 

二、下面我们单独创建一个例子来演示:

1、 创建工程,切换到ios平台、创建空gameobject,改名为Main,创建点击按钮触发购买的脚本,挂在Main上面。创建平台文件,下面创建子文件夹IOS。

2、 在设置里面修改包名,改为你自己在appstore后台创建的名称


IPADemo里面编写与ios通信代码以及购买代码,其中内购商品名称修改为自己appstore后台定义的:private stringproduct = "Package_2";

 

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

using System.Runtime.InteropServices;

 

public class IPADemo : MonoBehaviour {

 

         publicList<string> productInfo = new List<string>();

 

         privatestring product = "Package_2";

        

         [DllImport("__Internal")]

         privatestatic extern void TestMsg();//测试信息发送

        

         [DllImport("__Internal")]

         privatestatic extern void TestSendString(string s);//测试发送字符串

        

         [DllImport("__Internal")]

         privatestatic extern void TestGetString();//测试接收字符串

        

         [DllImport("__Internal")]

         privatestatic extern void InitIAPManager();//初始化

        

         [DllImport("__Internal")]

         privatestatic extern bool IsProductAvailable();//判断是否可以购买

        

         [DllImport("__Internal")]

         privatestatic extern void RequstProductInfo(string s);//获取商品信息

        

         [DllImport("__Internal")]

         privatestatic extern void BuyProduct(string s);//购买商品

        

         //测试从xcode接收到的字符串

         voidIOSToU(string s)

         {

                   Debug.Log("[MsgFrom ios]"+s);

         }

        

         //获取product列表

         voidShowProductList(string s){

                   productInfo.Add(s);

         }

        

         //获取商品回执

         voidProvideContent(string s)

         {

                   Debug.Log("[MsgFrom ios]proivideContent : "+s);

         }

         voidStart ()

         {

                   InitIAPManager();

         }

         voidUpdate ()

         {                          

         }

        

         voidOnGUI()

         {                

                   if(Btn("GetProducts")){

                            if(!IsProductAvailable())

                                     thrownew System.Exception("IAP not enabled");

                            productInfo= new List<string>();

                            RequstProductInfo(product);

                   }

                  

                   GUILayout.Space(40);

                  

                   for(inti=0; i<productInfo.Count; i++){

                            if(GUILayout.Button(productInfo[i],GUILayout.Height (100), GUILayout.MinWidth (200))){

                                     string[]cell = productInfo[i].Split('\t');

                                     Debug.Log("[Buy]"+cell[cell.Length-1]);

                                     BuyProduct(cell[cell.Length-1]);

                            }

                   }

         }

        

         boolBtn(string msg){

                   GUILayout.Space(100);

                   return       GUILayout.Button (msg,GUILayout.Width(200),GUILayout.Height(100));

         }

}

 

还需要在xcode里面编写内购代码然后复制到平台下,ios文件夹下:


只能在真机上才能出现内购窗,Unity中运行效果如下:


 

3、导出ios工程,在mac上xcode中打开:

4、加入依赖项,libz,storekit:


5、在真机上运行,首先获得商品列表,然后点击购买,然后在弹出的账号密码框里面修改为沙盒测试账号。即可测试购买成功。


 2个.h文件分别为:

1、IAPInterface.h


#import <Foundation/Foundation.h>


@interface IAPInterface : NSObject


@end


2、IAPManager.h


#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>


@interface IAPManager : NSObject<SKProductsRequestDelegate, SKPaymentTransactionObserver>{
    SKProduct *proUpgradeProduct;
    SKProductsRequest *productsRequest;
}


-(void)attachObserver;
-(BOOL)CanMakePayment;
-(void)requestProductData:(NSString *)productIdentifiers;
-(void)buyRequest:(NSString *)productIdentifier;


@end


 2个.m文件分别为:

1/IAPManager.m

#import "IAPManager.h"

@implementation IAPManager


-(void) attachObserver{
    NSLog(@"AttachObserver");
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}


-(BOOL) CanMakePayment{
    return [SKPaymentQueue canMakePayments];
}


-(void) requestProductData:(NSString *)productIdentifiers{
    NSArray *idArray = [productIdentifiers componentsSeparatedByString:@"\t"];
    NSSet *idSet = [NSSet setWithArray:idArray];
    [self sendRequest:idSet];
}


-(void)sendRequest:(NSSet *)idSet{
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:idSet];
    request.delegate = self;
    [request start];
}


-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    NSArray *products = response.products;
    
    for (SKProduct *p in products) {
        UnitySendMessage("Main", "ShowProductList", [[self productInfo:p] UTF8String]);
    }
    
    for(NSString *invalidProductId in response.invalidProductIdentifiers){
        NSLog(@"Invalid product id:%@",invalidProductId);
    }
    
    [request autorelease];
}


-(void)buyRequest:(NSString *)productIdentifier{
    SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}


-(NSString *)productInfo:(SKProduct *)product{
    NSArray *info = [NSArray arrayWithObjects:product.localizedTitle,product.localizedDescription,product.price,product.productIdentifier, nil];
    
    return [info componentsJoinedByString:@"\t"];
}


-(NSString *)transactionInfo:(SKPaymentTransaction *)transaction{
    
    return [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];
    
    //return [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSASCIIStringEncoding];
}


-(NSString *)encode:(const uint8_t *)input length:(NSInteger) length{
    static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    
    NSMutableData *data = [NSMutableData dataWithLength:((length+2)/3)*4];
    uint8_t *output = (uint8_t *)data.mutableBytes;
    
    for(NSInteger i=0; i<length; i+=3){
        NSInteger value = 0;
        for (NSInteger j= i; j<(i+3); j++) {
            value<<=8;
            
            if(j<length){
                value |=(0xff & input[j]);
            }
        }
        
        NSInteger index = (i/3)*4;
        output[index + 0] = table[(value>>18) & 0x3f];
        output[index + 1] = table[(value>>12) & 0x3f];
        output[index + 2] = (i+1)<length ? table[(value>>6) & 0x3f] : '=';
        output[index + 3] = (i+2)<length ? table[(value>>0) & 0x3f] : '=';
    }
    
    return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}


-(void) provideContent:(SKPaymentTransaction *)transaction{
    UnitySendMessage("Main", "ProvideContent", [[self transactionInfo:transaction] UTF8String]);
}


-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
                break;
            default:
                break;
        }
    }
}


-(void) completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"Comblete transaction : %@",transaction.transactionIdentifier);
    [self provideContent:transaction];
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


-(void) failedTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"Failed transaction : %@",transaction.transactionIdentifier);
    
    if (transaction.error.code != SKErrorPaymentCancelled) {
        NSLog(@"!Cancelled");
    }
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


-(void) restoreTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"Restore transaction : %@",transaction.transactionIdentifier);
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}




@end


2、#import "IAPInterface.h"
#import "IAPManager.h"


@implementation IAPInterface


void TestMsg(){
    NSLog(@"Msg received");


}


void TestSendString(void *p){
    NSString *list = [NSString stringWithUTF8String:p];
    NSArray *listItems = [list componentsSeparatedByString:@"\t"];
    
    for (int i =0; i<listItems.count; i++) {
        NSLog(@"msg %d : %@",i,listItems[i]);
    }
    
}


void TestGetString(){
    NSArray *test = [NSArray arrayWithObjects:@"t1",@"t2",@"t3", nil];
    NSString *join = [test componentsJoinedByString:@"\n"];
    
    
    UnitySendMessage("Main", "IOSToU", [join UTF8String]);
}


IAPManager *iapManager = nil;


void InitIAPManager(){
    iapManager = [[IAPManager alloc] init];
    [iapManager attachObserver];
    
}


bool IsProductAvailable(){
    return [iapManager CanMakePayment];
}


void RequstProductInfo(void *p){
    NSString *list = [NSString stringWithUTF8String:p];
    NSLog(@"productKey:%@",list);
    [iapManager requestProductData:list];
}


void BuyProduct(void *p){
    [iapManager buyRequest:[NSString stringWithUTF8String:p]];
}


@end


2016-04-11 13:50:45 u014528558 阅读数 2105

前言

喜❤欢❤请❤点❤赞

 

编程习惯

  1. 代码命名和书写规则可以参考alibaba tx netease都有出手册..这个很重要是共同开发的基础
  2. 单个类内容太多的时候,可以用partial分隔类
  3. 参数多的时候,可以封装为一个struct
  4. if else太长的时候,可以多换行能一次写完的都写在一起
  5. 不要在参数内嵌套方法 考虑到单行长度和debug的时候不知道错误出在哪里

使用Visual Studio作为编辑器

●安装 --- unity2017以后的版本,不自带Mono了,但安装Unity时可勾选安装vs,安装vs也可勾选安装Unity,官方互gay
●调试 --- 如果不会,必须学习,调试是快速debug的重要方法。vs可一键Unity断点调试,改完代码F5Unity那边也跑起来了

                          
●快捷键 --- 修改快捷键ctrl+w关闭当前窗口的方法,照着这样改成你自己的习惯,希望vs啥时候出个设置保存云..
●搜索 --- 使用“ Ctrl + ; ”  打开可以搜索类和方法,输入栏右方设置搜索是否包括方法;使用“ Ctrl+T/, ”可以搜索脚本内的方法
                    
●阅读 --- 可以使用使用 #region和#endregion,用于代码的缩放,对于代码阅读编辑相当好。
●标签 --- 灵活使用标签例如//TODO就可以标注将会做(其实是骗自己...)之后可以在 视图→任务列表 查看              
●DLL --- vs还可以生成解决方案DLL,然后拉过去或代码功能自动复制一份到UnityAsset内就可以给Unity使用了

 

Unity常用API

  1. 物体旋转和位移
    //3D旋转:
    (瞬间)
    transform.localRotation = Quaternion.Euler(new Vector3(x,y,z));
    transform.rotation = Quaternion.AngleAxis(30, Vector3.up);//绕轴旋转正方向30
    (缓慢目标)
    transform.rotation = Quaternion.Slerp( a , b , Time.deltime * speed);
    (缓慢速度)
    mRotaX = transform.localEulerAngles.x;
    mRotaY = transform.localEulerAngles.y;
    mRotaZ = transform.localEulerAngles.z;
    float deltaAngle = CommonData.DeltaTime * AngleSpeed;
    mRotaZ += deltaAngle;
    transform.localRotation = Quaternion.Euler(mRotaX, mRotaY, mRotaZ);
    (角度转换)
    Vector3 rotationVector3 = new Vector3(0f, 30f, 0f);
    Quaternion rotation = Quaternion.Euler(rotationVector3);
    transform.rotation = rotation;
    Quaternion.eulerAngles直接输出Quaternion的Vector3值
    --------------------- 
    //3D位移:
    (方向)
    transform.Translate(Vector3.back * FallingSpeed);
    transform.position = Vector3.MoveTowards(transform.position,tempUpdatePos, 
                          Time.deltaTime * DisShakeThreshold);
    (固定时间)
    transform.Translate(Vector3.normalize(tarPos-selfPos) *
             (Vector3.Distance(selfPos,tarPos)/(setTime * Time.deltime)));
    --------------------- 
    //2D方向移动:
    moveDirection = mTarget.transform.position - mSelf.transform.position;
    moveDirection.Normalize();
    float target = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg - 90f;
    mSelf.transform.rotation = 
    Quaternion.Slerp(mSelf.transform.rotation,  
    Quaternion.Euler(0, 0, target), turnSpeed * Time.deltaTime);
    _controller.SetForce(speedX,speedY));
    DOTween
     
  2. 按键Input
    void Update()
        {
            if (Input.GetKey("up"))
            {
                print("up arrow key is held down");
            }
    
            if (Input.GetKey("down"))
            {
                print("down arrow key is held down");
            }
        }
  3. OnGui按钮 与 Log
    void OnGUI()
        {
            if (GUI.Button(new Rect(10, 10, 150, 100), "I am a button"))
            {
                Debug.Log("<color=#9400D3> You clicked the button! </color>");
                //green 绿色	#008000ff    red 红色	#ff0000ff
                //white 白色	#ffffffff	 yellow 黄色	#ffff00ff
             
                /*转义符
                \’               单引号符 
                \”               单引号符 
                \\                反斜线符"\" 
                \n                换行
                \r                 回车 
                text在运行后\会自动变成了\\,所以无效解决方法:
                msgText.text = msg.Replace("\\n", "\n");
                */
            }
        }
  4. 资源加载

    资源加载API分为Editor下的和非Editor下的  其他平台路径各有区别
    //作为重要功能 深入研究会发现坑很多
     
    /*********************Editor******************/
    //1.Resource文件夹
    //同步
    GameObject prefab = Resources.Load("资源路径/名字") as GameObject;
    GameObject go = Instantiate(prefab) as GameObject;
    //异步
    Resources.LoadAsync<Object>(path);
    //2.非Resourece
    //路径为 Assets/XX/XX.prefab 这样的格式
    UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
    /*********************非Editor的其他******************/
    AssetBundle bundle = AssetBundle.LoadFromFile(path);
    Object obj = bundle.LoadAsset<GameObject>("XXXX");
  5. U3D射线射碰撞体与层

    //(检测是否碰撞)
    if (Physics.Raycast(origin, Vector3.down, 1f,1 << LayerMask.NameToLayer("Ground")))
    {
        //层的说明
        //int 1-31是未转化的层index 而用于射线LayerMask 是1<<int 运算后的 是一个很大的数
        // 1 << 10 打开第10的层。 等价于 1 << LayerMask.NameToLayer("Ground");
        //~(1 << 10) 打开除了第10之外的层。
        //~(1 << 0) 打开所有的层。
        //(1 << 10) | (1 << 8) 打开第10和第8的层。等价于 LayerMask.GetMask(("Ground", "Wall");
    }
    //(输出信息的)
    Ray ray = new Ray(transform.position, Vector3.down);
    RaycastHit hitInfo = new RaycastHit();
    if (Physics.Raycast(ray, out hitInfo, 30f, layerMask))
    {
        Debug.Log(hitInfo.point);
    }
    //物体
    gameObject.layer = (1-31层 或 LayerMask.NameToLayer("Ground"));
    //发射碰撞体
    Physics.OverXXX

    在Unity中每个GameObject都有Layer属性,默认的Layer都是Default。在Unity中可编辑的Layer共有24个(8—31层),官方已使用的是0—7层,默认不可编辑!

    LayerMask实际上是一个位码操作,在Unity3D中一共有32个Layer层,并且不可增加。

    位运算符
    按位运算符:~、|、&、^。位运算符主要用来对二进制位进行操作。

    逻辑运算符:&&、||、!。逻辑运算符把语句连接成更复杂的复杂语句。

    按位运算符:左移运算符<<,左移表示乘以2,左移多少位表示乘以2的几次幂。

  6. 协程IEnumerator
    void Start(){
        StartCoroutine(Test());
    }
    IEnumerator Test()
    {
        yield return null; // 下一帧再执行后续代码
        yield return 0; //下一帧再执行后续代码
        yield return 6;//(任意数字) 下一帧再执行后续代码
        yield break; //直接结束该协程的后续操作
        yield return asyncOperation;//等异步操作结束后再执行后续代码
        yield return StartCoroution(/*某个协程*/);//等待某个协程执行完毕后再执行后续代码
        yield return WWW();//等待WWW操作完成后再执行后续代码
        yield return new WaitForEndOfFrame();//等待帧结束,等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前执行
        yield return new WaitForSeconds(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间会受到Time.timeScale的影响);
        yield return new WaitForSecondsRealtime(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间不受到Time.timeScale的影响);
        yield return WaitForFixedUpdate();//等待下一次FixedUpdate开始时再执行后续代码
        yield return new WaitUntil()//将协同执行直到 当输入的参数(或者委托)为true的时候....如:yield return new WaitUntil(() => frame >= 10);
        yield return new WaitWhile()//将协同执行直到 当输入的参数(或者委托)为false的时候.... 如:yield return new WaitWhile(() => frame < 10);
    }
  7. Animator
    Animator可以被继承!
    Animator可以添加脚本处理状态!

    重置动画(播放动画第一帧)
    firstAnimName = GetComponent<Animator>().GetCurrentAnimatorClipInfo(0)[0].clip.name;
    GetComponent<Animator>().Play (firstAnimName,-1,0); 

    获取某个动画名字的时间长度
    private Animator animator;
    public void GetLengthByName(string name){
        float length = 0;
        AnimationClip[] clips = animator.runtimeAnimatorController.animationClips;
        foreach(AnimationClip clip in clips)    {
            if(clip.name.Equals(name))        {
                length = clip.length;            break;
            }
        }    Debug.Log(length);
    }
  8. 刚体

    Mass:质量(kg)
    Drag:空气阻力 越大越难推动
    Angular Drag:旋转阻力  越大越不会旋转 0无阻力 10以后基本无惯性
    Use Gravity:是否使用重力

    Constraints
    Freeze Position:锁定位置
    Freeze Rotation:锁定旋转
     
  9. 物体碰撞

    Edit-Project Settings-Physics 打开物理管理器(Physics Manager)里面可以设置层级相互间的碰撞
    碰撞条件是都带Collider 其中一个带rigidbody

    //触发器
    MonoBehaviour.OnTriggerEnter(Collider collider)当进入触发器
    MonoBehaviour.OnTriggerExit(Collider collider)当退出触发器
    MonoBehaviour.OnTriggerStay(Collider collider)当逗留触发器
     
    //碰撞信息检测:
    MonoBehaviour.OnCollisionEnter(Collision collision) 当进入碰撞器
    MonoBehaviour.OnCollisionExit(Collision collision) 当退出碰撞器
    MonoBehaviour.OnCollisionStay(Collision collision)  当逗留碰撞器

     

  10. 平台预处理
    #if UNITY_EDITOR  
        platform = "hi,大家好,我是在unity编辑模式下";  
    #elif UNITY_SERVER
    #elif UNITY_IPHONE  
        platform="hi,大家好,我是IPHONE平台";  
    #elif UNITY_ANDROID  
        platform="hi,大家好,我是ANDROID平台";  
    #elif UNITY_STANDALONE_OSX  
    #elif UNITY_STANDALONE_WIN  
        platform="hi,大家好,我是Windows平台";  
    #endif  
        Debug.Log("Current Platform:" + platform);  
  11. DoFile 读取一个Txt
    string localConfig = Application.dataPath + "/Config/123.txt";
    if (File.Exists(localConfig))
    {
        string[] strs = File.ReadAllLines(localConfig);
    }
  12. Editor

    选中物体保存为预设
    	[UnityEditor.MenuItem("Tools/Apply Selected Prefabs")]
    	static void ApplySelectedPrefabs(){
    		//获取选中的gameobject对象
    		GameObject [] selectedsGameobject= Selection.gameObjects;		
    		GameObject prefab = PrefabUtility.FindPrefabRoot (selectedsGameobject[0]);		
    		for (int i = 0; i < selectedsGameobject.Length; i++) {
    			GameObject obj=selectedsGameobject[i];			
    			UnityEngine.Object newsPref= PrefabUtility.GetPrefabObject(obj);			
    			//判断选择的物体,是否为预设
    			if(PrefabUtility.GetPrefabType(obj)  == PrefabType.PrefabInstance){				
    				UnityEngine.Object parentObject = PrefabUtility.GetPrefabParent(obj); 				
    				//string path = AssetDatabase.GetAssetPath(parentObject);//获取路径
    PrefabUtility.ReplacePrefab(obj,parentObject,ReplacePrefabOptions.ConnectToPrefab);//替换预设					
    				AssetDatabase.Refresh();//刷新
    			}	
    		}		
    	}


     
  13. Inspector面板自定义
    用代码重新绘制了Inspector,并赋值,所以注意不能使用Ctrl+Z
    [CustomEditor(typeof(你的代码))]
    public class 你的代码Editor : Editor
    {
        SerializedObject myScript;
        SerializedProperty bool变量;
        SerializedProperty 任意变量;
        SerializedProperty 数组变量;
    
        void OnEnable()
        {
            myScript = new SerializedObject(target);
            bool变量 = myScript.FindProperty("bool变量");
            任意变量 = myScript.FindProperty("任意变量");
            数组变量 = myScript.FindProperty("数组变量");
        }
    
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();
            myScript.Update();
            if (bool变量.boolValue)
            {
                EditorGUILayout.LabelField("面板提示");
                EditorGUILayout.PropertyField(任意变量);
            }
            else
            {
                EditorGUILayout.LabelField("面板提示");
                数组变量.arraySize = EditorGUILayout.IntField("Length", 数组变量.arraySize);
                for (int i = 0; i < 数组变量.arraySize; i++)
                    EditorGUILayout.PropertyField(数组变量.GetArrayElementAtIndex(i));
            }
            myScript.ApplyModifiedProperties();
        }
    }
  14.  

  15. 画线画圆画弧
    ①Debug.DrawLine

    public float radius;
    public float angle;
    public int smooth = 20;
    public Vector3 dir = Vector3.right;
    public bool drawSide = true;
    
    void Update()
    {
        Vector3 start = Quaternion.Euler(0, 0, -angle) * dir * radius;
        Vector3 firstPos = Vector3.zero;
        Vector3 lastPos = Vector3.zero;
        for (int i = 0; i <= smooth; i++)
        {
            firstPos = transform.position + Quaternion.Euler(0,0,(angle*2/smooth)*i)*start;
            lastPos = transform.position + Quaternion.Euler(0,0,(angle*2/smooth)*(i+1))*start;
            Debug.DrawLine(firstPos, lastPos, Color.red);
        }
        if (drawSide)
        {
            Debug.DrawLine(transform.position, transform.position + start, Color.red);
            Debug.DrawLine(transform.position, lastPos, Color.red);
        }
    }


    ②LineRender

    private LineRenderer line;//画线
    line = this.gameObject.AddComponent<LineRenderer>();
    //只有设置了材质 setColor才有作用
    line.material = new Material(Shader.Find("Particles/Additive"));
    line.SetVertexCount(2);//设置两点
    line.SetColors(Color.yellow, Color.red); //设置直线颜色
    line.SetWidth(0.1f, 0.1f);//设置直线宽度
    
    //设置指示线的起点和终点
    line.SetPosition(0, start.position);
    line.SetPosition(1, end.position);
    
    public float R;//半径
    public int N;//不要超过45
    
    line.SetVertexCount(N+1);//这里要加1,达成闭合曲线
    for (int i = 0; i < N + 1; i++)
    {
      float x = R * Mathf.Cos((360 / N * i) * Mathf.Deg2Rad) + transform.position.x; //确定x坐标
      float z = R * Mathf.Sin((360 / N * i) * Mathf.Deg2Rad) + transform.position.z; //确定z坐标
      line.SetPosition(i, new Vector3(x, transform.position.y, z));         
    }

     

  16. 控制全局音量
    通过 AudioListener.volume来控制场景场景音量
    Unity推出了自己声音引擎
    也可以用热门的引擎 如:Wwise..
     
  17. UGUI

    进度条 -- 通过改变UI的FillAmount值 UnityEngine.UI.Image healthBar.fillAmount = 0.0f;

    艺术字体 -- 对于Text 使用材质和Outline可以制作

    Mask -- 可以遮挡 对于制作一些动画比较好

    自动排列用 Layout Group 如果要适配改变大小 都要加Content Size Fitter 要改变的设置为preferred size;
     

  18. Unity播放透明动画

    ①UnityAnimation播放,直接全选png序列帧图(卡超级久) 然后拖到面板上(卡很久),会自动生成序列播放的animator。但我发现超过300图之后有可能会出现不明报错(大概因为输出问题),1k张感觉就没什么希望了,最重要的是在unity里1080p的图转成sprite,都会变成10m一张,导致项目发布之后更是奇大无比。
    ②播放mov,可以尝试播带透明通道的mov,但需要一个shader,具体我不会写,在网上找到的也不太好用,各种问题(抠色会失真和有噪点,提取透明or黑白遮罩会出现不同步
    LoadResources,先load资源文件夹内的,然后播放。load的东西如果超过2G(C#设定)则不能加载
    ④LoadAssets。因为是一张张地load,不会出现太大而不加载的问题。目前使用这个办法播放

  19. Lighting

    设置窗口(Window->Lighting->Settings)是主要控制unity全局光照(GlobalIllumination GI)的地方。尽管GI的默认设置已经有了很好的效果,lighting设置面板的一些属性可以调节GI处理的很多方面,从而可以定制场景或者优化场景的质量、速度和存储空间。窗口还包括环境光、光晕、雾效、烘焙的设置。

    可使用Class.UnityEngine.RenderSettings代码改变这些

  20. Debug
    日志路径 C:\Users\Administrator\AppData\LocalLow\DefaultCompany
  21. 待续..
     

Unity特性

1.unity具有特殊意义的文件夹

link

2.unity时间函数执行顺序

3.持续更新;

 

C#相关API

  1. Only one exe只允许打开一个程序
    int count=Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length ;
    if (count> 1)
    {
        // MessageBox.Show(count.ToString());调试使用    
        Process.GetCurrentProcess().Kill();//杀掉当前
    }

     

  2.  启动一个进程
     

    一个C#的API  可以用于打开一个网页..应用..或者进程..或者控制面板内的功能..例如打印..

    public void PrintFile()
    {
        System.Diagnostics.Process process = new System.Diagnostics.Process(); //系统进程
        process.StartInfo.CreateNoWindow = false; //不显示调用程序窗口
        process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;//
        process.StartInfo.UseShellExecute = true; //采用操作系统自动识别模式
        process.StartInfo.FileName=path; //要打印的文件路径
        process.StartInfo.Verb="print"; //指定执行的动作,打印:print打开:open…………
        process.Start(); //开始打印
    }

     

  3. 改变鼠标位置和隐藏鼠标
     

    using System.Runtime.InteropServices;
    [DllImport("user32.dll")]
    public static extern int SetCursorPos(int x, int y);
    Void XX(){ SetCursorPos(0, 0);  }
    [DllImport("user32.dll")] 
    public static extern int SetCursorPos(int x, int y); 
    void Start () 
    {
        //隐藏
        Cursor.visible = false;
    }
  4.  

 

数学相关

向量与夹角与三角函数

//U3D  Vector3.forward 实际是(0,0,1) 向前的意思 也就是右上角Z轴方向
//forward是蓝色箭头方向 back是反方向  right是红色箭头方向 left反之 up是绿色箭头方向 down反之

Vector3 vector = targetPos - originPos;//任意两点向量 = V3目标位置 - V3起点位置
float angle = Vector3.Angle(from, to); //向量夹角 v3.angle(向量A,向量B)
Vector3.Normalize;//单位向量

//Deg2Rad 度转弧度 ; Rad2Deg 弧度转度
//求角度的三角函数值
Mathf.Sin(Mathf.Deg2Rad * angle);
//求变长求角度
Mathf.Atan2(y,x) * Mathf.Rad2Deg;

 

 

其他

  1. uLua使用方法

    注意AppConst里的LuaBundleModel变量,如果想使用AssetBundle模式,该值置为true,但是每次修改lua代码,需要执行菜单栏上的LuaFramework->Build XXX Resource,将lua资源打包,否则,你修改的lua逻辑不会更新并执行。

    如果LuaBundleModel置为false,则lua代码会时时更新。

    此外,修改C#代码,一定不要忘了执行菜单Lua->Clear wrap files,然后重新生成Gen Wrap + Binder

    确定逻辑无误后,注册C#类,打开"CustomSettings.cs",注册我们的类。"_GT(typeof(XXXXXX)),"。最后,执行上面第2步的Lua->Clear wrap files,Gen Wrap + Binder。此时,会生成一个叫MyHelloWorldWrap.cs的文件,在Assets\LuaFramework\ToLua\Source\Generate目录下,其实所有在CustomSettings中注册的C#类都会生成Wrap文件,保存在该目录下。

  2. Windows工具
    项目同步一般用svn或git主要是免费 大公司用收费的软件
    AutoHotKey作为快捷键操作打开
     
  3. Mac鸡工具 

    连接:可以使用vnc viewer
    项目管理:Cornerstone

  4. 持续更新...

 

2017-09-03 20:36:33 MacYosef 阅读数 862

记录并解析unity中经常用到的API

1.Camera

1.pixelRect

pixelRect属性:摄像机渲染区间
此属性用于设置camera被渲染到屏幕中间的坐标位置。pixelRect属性与rect功能类似,不同的是pixelRect以实际像素大小来设置显示视口的位置,而rect以单位化方式设置显示视口的位置。
例设置camera.pixelRect (x0,y0,w,h), x0为视口右移的像素大小,y0为视口上移的像素大小,w为camera.pixelWidth,h的值为camera.pixelHeight.

2.Mathf

1.PingPong

public static float PingPong(float t,float length)
此方法用于模拟乒乓球的往复运动。设f = Mathf.PingPong (t, h),其中f 、t、h均为 float值类型。
(1)若h>0,则

  f=|t| % h,        当 |t| % 2h <= h时
  f=h - |t| % h,    当 |t| % 2h > h时

(1)若h<0,则

  f=2h + |t| % |2h|,        当| t | % |2h| <= h时
  f= - |t| % |2h|,    当| t | % |2h| > h时

要想让物体进行PingPong运动, t 必须是一个不断改变的值 ,常用Time.time 来当作 t 值。f 会从 0 开始变化到 h ,然后再从 h 变化到0.

3.Rigidbody

1.SweepTest

语法:

public bool SweepTest(Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal); 
参数 作用
direction 刚体扫过的方向
hitInfo 如果返回true,hitInfo将包含更多关于被击中位置碰撞体的信息
maxDistance 扫到的长度,默认为无穷大

2.WakeUp

语法:public void WakeUp( );
功能:此方法用于将刚体从休眠状态唤醒。要将刚体从休眠状态唤醒,除了调用此方法以外,在以下四种情况发生时,刚体会被自动唤醒:

  • 其他刚体与休眠中的刚体发生了碰撞
  • 使用关节连接的其他刚体发生了移动
  • 刚体的属性发生了改变
  • 给休眠中的刚体施加了一个外力

3.OnTriggerXXX和OnCollisionXXX

设现有A、B两个物体,且A物体正向B移动,B物体保持静止状态,如图所示:

这里写图片描述

详解:

  • 若A中无Rigidbody组件,则B中无论是否含有Rigidbody组件,A都将穿过B物体,并且A和B脚本中的OnTriggerXXX和OnCollisionXXX方法都不会调用。

  • 若A中含有Rigidbody组件,则B中无论是否含有Rigidbody组件,只要B中含有Collider类组件,A和B脚本中的OnTriggerXXX或OnCollisionXXX方法就会被调用。到底调用哪一种要看A和B物体中的Collider类组件中的IsTrigger是否被选中。总之,要激活OnTriggerXXX或OnCollisionXXX方法必须使移动的物体中含有Rigidbody组件

  • 若A中含有Rigidbody组件,B中含有Collider类组件,当A和B物体中的Collider类组件的IsTrigger都没有选中时,A和B脚本中(两个物体)的OnCollisionXXX类的方法就会被调用

  • 若A中含有Rigidbody组件,B中含有Collider类组件,当A和B物体中的Collider类组件的IsTrigger至少有一个被选中时,A和B脚本中(两个物体)的OnTriggerXXX静态方法会被调用

4.Transform

1.localPosition

public Vector3 localPosition { get; set; }

此属性用于设置或返回GameObject在局部坐标系中的位置,若无父级对象则和属性Transform.position返回值相同。

transform.localPosition的值受父级对象lossyScale的影响,当transform.localPosition的值增加1时,transform.position的值的增量不一定是1,而是在相对父级坐标系中增加了父级的lossyScale倍大小的值。例如物体cube2的父级是cube1,cube1的父级是cube0,cube1的localScale为Vector3(c1x,c1y,c1z),cube0的localScale为Vector3(c0x,c0y,c0z),假设cube0和cube1都没有发生旋转,则GameObject对象在世界坐标系中的位置为:
transform.position.x = cube0.localPosition.x+cube1.localPosition.x*c0x+transform.localPosition.x*c1x*c0x;
transform.position.y和transform.position.z与此相同。
当物体cube2的局部坐标值localPosition增加Vector3(tx,ty,tz)时,其世界坐标值position的增加值分别为:
_x=tx*c1x*c0x;
_y=ty*c1y*c0y;
_z=tz*c1z*c0z;

5.Vector3

1.RotateTowards

基本语法:public static Vector3 RotateTowards( Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta);
其中参数current为起始点坐标,参数target为目标点坐标,参数maxRadiansDelta为角度旋转系数,参数maxMagnitudeDelta为模长系数。

功能说明:
此方法用于返回从参数current到target的球形旋转插值向量,此方法可控制插值向量的角度和模长。例如,设有Vector3实例A、B和C,有float类型数值R和L,向量A和B的夹角的弧度为e,则执行以下程序代码后
C=Vector3.RotateTowards(A,B,R,L);

  • 当R∈[0,e]时,向量C和A的弧度为R,即当R从0增加到e时,向量C与A的角度也会线性增加到e。向量C的模长为向量A的模长加上L的值,即|C|=|A|+L.
  • 当R<0时,向量C会沿着A到B的反向量旋转。
  • 当R>e时,参数R以e来计算角度,即R大于e时C与B的方向相同。
  • 总之,R值决定了向量C的方向,而L影响了C的模长,无论L取值多少,当R>e时向量C与B的方向总是相同的。
  • 向量A、B和C在同一平面上。

2.Slerp

基本语法:public static Vector3 Slerp(Vector3 from, Vector3 to, float t);
其中参数from为插值起始点坐标,参数to为插值结束点坐标,参数t为插值系数。
功能说明:此方法用于返回从参数from点到参数to点的球形插值向量。例如,设现有Vector3实例A和B,A ( ax,ay,az), B( bx,by,bz) A和B的夹角为e,则执行如下代码后
Vector C = Vector3.Slerp(from,to,t);

  • 当t<=0时,向量C=A;
  • 当t>=1时,向量C=B;
  • 当t从0增加到1时,向量C会从起始点A绕着A*B(即向量A和B的叉乘)的方向匀速移动到向量B,此处的匀速时指角度旋转的匀速,即向量C与B的夹角k=e*(1-t),这样便可以确定向量C的方向。而向量C的模长计算公式则为:
    |C|=ax2+ay2+az2+(bx2+by2+bz2ax2+ay2az2)t
  • 当向量A和B中的某个分量的值都为0时,比如它们的y轴分量都为0,即ay=by=0时,则A将绕着y轴在xy平面匀速旋转向B移动,并且在移动过程中C.y的值始终为0.
2018-04-06 17:29:51 wuyt2008 阅读数 2350

unity3D与web api交互最大的用处,除了下载各种资源以外,大概就是可以记录一些简单的数据。如果只是做个简单的打飞机的游戏,想记录下最高成绩的话,用web api足够了,而且,web的资源丰富,改起来快。


unity3D访问web api关键点就以下2处

1、UnityWebRequest:这个方法包含了所有的内容,替代就的WWW的那个类。

2、如果存在登录,需要在登录以后将传回来的Set-Cookie存下来,下次请求的时候包含在头部用Cookie发出去。


这个项目已经传到了github,地址:https://github.com/wuyt/RCSelesia

另外,写了这个测试用的web api,asp.net core写的,地址:https://github.com/wuyt/RCOhanashi


另外,因为对跨站攻击的有些内容不很熟悉,所以,没有考虑跨站攻击的问题。


直接上代码

using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class WebApi : MonoBehaviour
{

    string cookie;

    public InputField inputRegisterEmail;
    public InputField inputRegisterPassword;

    public InputField inputLoginEmail;
    public InputField inputLoginPassword;

    public InputField inputGetID;

    public InputField inputPostName;
    public InputField inputPostNumber;

    public InputField inputPutID;
    public InputField inputPutName;
    public InputField inputPutNumber;

    public InputField inputDeleteID;

    // Use this for initialization
    void Start()
    {
        cookie = "";
    }

    public void Register()
    {
        StartCoroutine(IERegister());
    }

    IEnumerator IERegister()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/account/register";
            www.method = UnityWebRequest.kHttpVerbPOST;

            www.SetRequestHeader("Cookie", cookie);

            byte[] bodyRaw = Encoding.UTF8.GetBytes("{\"Email\": \"" + inputRegisterEmail.text + "\",\"password\": \"" + inputRegisterPassword.text + "\",\"ConfirmPassword\": \"" + inputRegisterPassword.text + "\"}");
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.SetRequestHeader("Content-Type", "application/json");

            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void Login()
    {
        StartCoroutine(IELogin());
    }

    IEnumerator IELogin()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/account/login";
            www.method = UnityWebRequest.kHttpVerbPOST;

            www.SetRequestHeader("Cookie", cookie);

            byte[] bodyRaw = Encoding.UTF8.GetBytes("{\"Email\": \"" + inputLoginEmail.text + "\",\"password\": \"" + inputLoginPassword.text + "\",\"remeberme\": false }");
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.SetRequestHeader("Content-Type", "application/json");
            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                cookie = www.GetResponseHeader("Set-Cookie");
                Debug.Log(cookie);

                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void LoginOut()
    {
        StartCoroutine(IELoginOut());
    }

    IEnumerator IELoginOut()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/account/loginOut";
            www.method = UnityWebRequest.kHttpVerbGET;

            www.SetRequestHeader("Cookie", cookie);

            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                cookie = www.GetResponseHeader("Set-Cookie");
                Debug.Log(cookie);

                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void AllAnimes()
    {
        StartCoroutine(IEAllAnimes());
    }

    IEnumerator IEAllAnimes()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/Animes";
            www.method = UnityWebRequest.kHttpVerbGET;

            www.SetRequestHeader("Cookie", cookie);

            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void Anime()
    {
        StartCoroutine(IEAnime());
    }

    IEnumerator IEAnime()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/Animes/" + inputGetID.text;
            www.method = UnityWebRequest.kHttpVerbGET;

            www.SetRequestHeader("Cookie", cookie);

            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);


                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void PostAnime()
    {
        StartCoroutine(IEPostAnime());
    }

    IEnumerator IEPostAnime()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/animes";
            www.method = UnityWebRequest.kHttpVerbPOST;

            www.SetRequestHeader("Cookie", cookie);

            byte[] bodyRaw = Encoding.UTF8.GetBytes("{\"Name\": \"" + inputPostName.text + "\",\"Number\": \"" + inputPostNumber.text + "\",\"DateTime\": \"2011 - 08 - 01T00: 00:00\",\"IndexShow\":false}");
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.SetRequestHeader("Content-Type", "application/json");
            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void PutAnime()
    {
        StartCoroutine(IEPutAnime());
    }

    IEnumerator IEPutAnime()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/animes/" + inputPutID.text;
            www.method = UnityWebRequest.kHttpVerbPUT;

            www.SetRequestHeader("Cookie", cookie);

            byte[] bodyRaw = Encoding.UTF8.GetBytes("{\"ID\":" + inputPutID.text + ",\"Name\": \"" + inputPutName.text + "\",\"Number\": \"" + inputPutNumber.text + "\",\"DateTime\": \"2011 - 08 - 01T00: 00:00\",\"IndexShow\":false}");
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.SetRequestHeader("Content-Type", "application/json");
            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }

    public void DeleteAnime()
    {
        StartCoroutine(IEDeleteAnime());
    }

    IEnumerator IEDeleteAnime()
    {
        using (UnityWebRequest www = new UnityWebRequest())
        {
            www.url = "http://localhost:2572/api/Animes/" + inputDeleteID.text;
            www.method = UnityWebRequest.kHttpVerbDELETE;

            www.SetRequestHeader("Cookie", cookie);

            www.downloadHandler = new DownloadHandlerBuffer();

            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);


                // Or retrieve results as binary data
                //byte[] results = www.downloadHandler.data;
            }
        }
    }
}


2016-03-11 17:38:29 zld406504302 阅读数 484
unity3d 安装了一个多月了,一直忙,也没时间了解,一个demo写了一个月了,还没搞完,哈哈。今天突然想起学习中遇到一个棘手的问题,api帮助文档获取不到。当时google了一下,是5.0的一个官方bug。unity里api文档查找策略是优先查找本地,本地找不到会访问官方api帮助文档。由于5.0的访问地址进行了调整,本地api文档路径没有变,导致无法获取本地文档,官方的路径也是错误的。下面贴出解决办法,本人是在macbook上操作的,所以windows要调整路径。


#####(一)查看unity帮助文档下载路径
unity>help>Scripting Reference 
```
file:///Applications/Unity/Unity.app/Contents/Documentation/en/ScriptReference/index.html
```


#####(二)查看MonoDevelop中api路径
光标定位到class name上,按快捷键cmd+'
```
http://unity3d.com/cn/support/documentation/ScriptReference/30_search.html?q=PlayerController
```


#####(三)构建本地api文档访问目录
从一和二综合来看,可以猜测,unity实际访问的应该是
```
file:///Applications/Unity/Documentation/ScriptReference/MonoBehaviour.html
```
将/Applications/Unity/Unity.app/Contents/Documentation/en/中内容放到
/Applications/Unity/Documentation/下,


#####(四)测试
重复(二),顺利的话,会直接访问到本地的api文档地址




转载请保留出处:mac unity3d api获取不到问题





Unity 3D - 常用API

阅读数 679

Unity3D常用API

阅读数 3847

没有更多推荐了,返回首页