文档章节

http://blog.csdn.net/qq15233635728/article/details/46326951

中国人叔叔
 中国人叔叔
发布于 2017/02/08 15:05
字数 1290
阅读 61
收藏 0

利用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

本文转载自:

共有 人打赏支持
中国人叔叔
粉丝 0
博文 6
码字总数 3556
作品 0
松江
Android2.4.7启动结构简述

作者: 孔祥辉 本图针对2.3.7版安卓系统的启动过程,进行简单建模以促进对整个安卓系统的了解。请注意图中的两个叉子(fork)。 参考资料: http://blog.csdn.net/maxleng/article/details/5...

晨曦之光
2012/03/14
86
0
Echarts数据可视化全解

全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 http://blog.csdn.net/luanpeng825485697/article/details/76691965 Echarts数据可视化开发参数配置全解 http://blo...

luanpeng825485697
2017/08/18
0
0
Lua一系列很好的技术分享文章链接: 快速掌握 Lua 5.3

Lua一系列很好的技术分享文章链接: 快速掌握 Lua 5.3 --- 该系列文章作者: VermillionTear 作者博客专栏: 快速掌握Lua 5.3(http://blog.csdn.net/column/details/quicklymasterlua5-3.html)...

FreeBlues
2016/05/29
54
2
MySQL大小写问题的简单说明(关键字/函数/表名)(转)

MySQL语句中字母大小写规则随着语句元素的不同而变化,同时还要取决于MySQL服务器主机上的操作系统。 SQL关键字与函数名 关键字和函数名不区分字母的大小写。如、abs、bin、now、version、f...

easonjim
2017/09/15
0
0
springmvc+mybatis学习笔记(汇总)

springmvc+mybatis学习笔记(汇总) 标签 : springmvc mybaits [TOC] 笔记分为两大部分:mybatis和springmvc mybatis springmvc 笔记内容主要是mybatis和springmvc的一些基本概念和使用方法,...

brianway
2016/03/30
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

Java GC机制详解

垃圾收集 Garbage Collection 通常被称为“GC”,本文详细讲述Java垃圾回收机制。 导读: 1、什么是GC 2、GC常用算法 3、垃圾收集器 4、finalize()方法详解 5、总结--根据GC原理来优化代码 ...

小刀爱编程
19分钟前
1
0
ORACEL学习--理解over()函数

二、理解over()函数 1.1、两个order by的执行时机 分析函数是在整个sql查询结束后(sql语句中的order by的执行比较特殊)再进行的操作, 也就是说sql语句中的order by也会影响分析函数的执行结果...

spinachgit
21分钟前
1
0
抽象语法树

import asttree = ast.parse('def hello_word(): print("hello world!")')def hell_word(): print(1)print(ast.dump(tree)) 语法树 可以基于这个实现另外一种编程语言...

colin_86
21分钟前
1
0
Akka查询设备组《fourteen》译

加入以下依赖到项目中: 介绍: 到目前为止,我们看到的会话模式很简单,因为它们要求Actor保持很少或没有状态。 特别: 设备actor返回一个读数,不需要更改状态。 记录温度,更新单个字段。...

woshixin
28分钟前
1
0
wait、notify、notifyAll 的用法

wait()、notify()、notifyAll()是三个定义在Object类里的方法,用来控制线程的状态。这三个方法最终调用的都是jvm的native方法, 随着jvm运行平台的不同可能有些许差异。 wait(): 使持有该对...

noob_fly
30分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部