首页 文章 精选 留言 我的

精选列表

搜索[通用教育],共10006篇文章
优秀的个人博客,低调大师

【熵增教育】探索SpringBoot中的SpringMVC——熵增学院

spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc。spring mvc 是只是spring 处理web层请求的一个模块。因此他们的关系大概就是这样:spring mvc < spring <springboot。 理清SpringBoot与SpringMVC的关系 Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的 ioc和 aop ioc 提供了依赖注入的容器 aop ,解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能。 Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。 于是为了简化开发者的使用,从而创造性地推出了Spring boot,约定优于配置,简化了spring的配置流程。 说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。 大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。 然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot。 Spring MVC的功能Spring MVC提供了一种轻度耦合的方式来开发web应用。Spring MVC是Spring的一个模块,式一个web框架。 通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。 Spring Boot的功能Spring Boot实现了自动配置,降低了项目搭建的复杂度。 众所周知Spring框架需要进行大量的配置,Spring Boot引入自动配置的概念,让项目设置变得很容易。 Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。 同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。Spring Boot只是承载者,辅助你简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和你上面描述的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。对使用者来说,换用Spring Boot以后,项目初始化方法变了,配置文件变了,另外就是不需要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化。 所以,用最简练的语言概括就是: Spring 是一个“引擎”; Spring MVC 是基于Spring的一个 MVC 框架 ; Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。 Spring MVC自动配置 Spring Boot为Spring MVC提供的auto-configuration适用于大多数应用,并在Spring默认功能上添加了以下特性: 引入ContentNegotiatingViewResolver和BeanNameViewResolverbeans。 对静态资源的支持,包括对WebJars的支持。 自动注册Converter,GenericConverter,Formatterbeans。 对HttpMessageConverters的支持。 自动注册MessageCodeResolver。 对静态index.html的支持。 对自定义Favicon的支持。 自动使用ConfigurableWebBindingInitializerbean。 如果保留Spring Boot MVC特性,你只需添加其他的MVC配置(拦截器,格式化处理器,视图控制器等)。你可以添加自己的WebMvcConfigurerAdapter类型的@Configuration类,而不需要注解@EnableWebMvc。如果希望使用自定义的RequestMappingHandlerMapping,RequestMappingHandlerAdapter,或ExceptionHandlerExceptionResolver,你可以声明一个WebMvcRegistrationsAdapter实例提供这些组件。 如果想全面控制Spring MVC,你可以添加自己的@Configuration,并使用@EnableWebMvc注解。 HttpMessageConverters Spring MVC使用HttpMessageConverter接口转换HTTP请求和响应,合适的默认配置可以开箱即用,例如对象自动转换为JSON(使用Jackson库)或XML(如果Jackson XML扩展可用,否则使用JAXB),字符串默认使用UTF-8编码。 可以使用Spring Boot的HttpMessageConverters类添加或自定义转换类: importorg.springframework.boot.autoconfigure.web.HttpMessageConverters; importorg.springframework.context.annotation.*; importorg.springframework.http.converter.*; @Configuration publicclassMyConfiguration{ @Bean publicHttpMessageConverterscustomConverters(){ HttpMessageConverter<?>additional=... HttpMessageConverter<?>another=... returnnewHttpMessageConverters(additional,another); } } 上下文中出现的所有HttpMessageConverterbean都将添加到converters列表,你可以通过这种方式覆盖默认的转换器列表(converters)。 自定义JSON序列化器和反序列化器 如果使用Jackson序列化,反序列化JSON数据,你可能想编写自己的JsonSerializer和JsonDeserializer类。自定义序列化器(serializers)通常通过Module注册到Jackson,但Spring Boot提供了@JsonComponent注解这一替代方式,它能轻松的将序列化器注册为Spring Beans。 MessageCodesResolver Spring MVC有一个实现策略,用于从绑定的errors产生用来渲染错误信息的错误码:MessageCodesResolver。Spring Boot会自动为你创建该实现,只要设置spring.mvc.message-codes-resolver.format属性为PREFIX_ERROR_CODE或POSTFIX_ERROR_CODE(具体查看DefaultMessageCodesResolver.Format枚举值)。 静态内容 默认情况下,Spring Boot从classpath下的/static(/public,/resources或/META-INF/resources)文件夹,或从ServletContext根目录提供静态内容。这是通过Spring MVC的ResourceHttpRequestHandler实现的,你可以自定义WebMvcConfigurerAdapter并覆写addResourceHandlers方法来改变该行为(加载静态文件)。 在单机web应用中,容器会启动默认的servlet,并用它加载ServletContext根目录下的内容以响应那些Spring不处理的请求。大多数情况下这都不会发生(除非你修改默认的MVC配置),因为Spring总能够通过DispatcherServlet处理这些请求。 你可以设置spring.resources.staticLocations属性自定义静态资源的位置(配置一系列目录位置代替默认的值),如果你这样做,默认的欢迎页面将从自定义位置加载,所以只要这些路径中的任何地方有一个index.html,它都会成为应用的主页。 此外,除了上述标准的静态资源位置,有个例外情况是Webjars内容。任何在/webjars/**路径下的资源都将从jar文件中提供,只要它们以Webjars的格式打包。 注如果你的应用将被打包成jar,那就不要使用src/main/webapp文件夹。尽管该文件夹是通常的标准格式,但它仅在打包成war的情况下起作用,在打包成jar时,多数构建工具都会默认忽略它。 Spring Boot也支持Spring MVC提供的高级资源处理特性,可用于清除缓存的静态资源或对WebJar使用版本无感知的URLs。 如果想使用针对WebJars版本无感知的URLs(version agnostic),只需要添加webjars-locator依赖,然后声明你的Webjar。以jQuery为例,"/webjars/jquery/dist/jquery.min.js"实际为"/webjars/jquery/x.y.z/dist/jquery.min.js",x.y.z为Webjar的版本。 注如果使用JBoss,你需要声明webjars-locator-jboss-vfs依赖而不是webjars-locator,否则所有的Webjars将解析为404。 以下的配置为所有的静态资源提供一种缓存清除(cache busting)方案,实际上是将内容hash添加到URLs中,比如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>: spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/** 注实现该功能的是ResourceUrlEncodingFilter,它在模板运行期会重写资源链接,Thymeleaf,Velocity和FreeMarker会自动配置该filter,JSP需要手动配置。其他模板引擎还没自动支持,不过你可以使用ResourceUrlProvider自定义模块宏或帮助类。 当使用比如JavaScript模块加载器动态加载资源时,重命名文件是不行的,这也是提供其他策略并能结合使用的原因。下面是一个"fixed"策略,在URL中添加一个静态version字符串而不需要改变文件名: spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/** spring.resources.chain.strategy.fixed.enabled=true spring.resources.chain.strategy.fixed.paths=/js/lib/ spring.resources.chain.strategy.fixed.version=v12 使用以上策略,JavaScript模块加载器加载"/js/lib/"下的文件时会使用一个固定的版本策略"/v12/js/lib/mymodule.js",其他资源仍旧使用内容hash的方式<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>。查看ResourceProperties获取更多支持的选项。 欢迎页面 Spring Boot支持静态和模板欢迎页面。它首先index.html在配置的静态内容位置中查找文件。如果找不到,则会查找index模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。、 自定义Favicon Spring Bootfavicon.ico在配置的静态内容位置和类路径的根目录(按此顺序)中查找a。如果存在这样的文件,它会自动用作应用程序的图标。 路径匹配和内容协商 Spring MVC可以通过查看请求路径并将它匹配到应用程序中定义的映射(例如@GetMappingController方法上的注释),将传入的HTTP请求映射到处理程序。 Spring Boot选择默认禁用后缀模式匹配,这意味着请求"GET /projects/spring-boot.json"不会匹配@GetMapping("/projects/spring-boot")映射。这被认为是Spring MVC应用程序的最佳实践。此功能在过去对于没有发送正确的“Accept”请求标头的HTTP客户端来说非常有用;我们需要确保将正确的内容类型发送到客户端。如今,内容协商更可靠。 还有其他一些方法可以处理不一致地发送适当的“接受”请求标头的HTTP客户端。我们可以使用查询参数来确保类似的请求"GET /projects/spring-boot?format=json"将映射到@GetMapping("/projects/spring-boot")以下内容,而不是使用后缀匹配: spring.mvc.contentnegotiation.favor-parameter=true #我们可以更改参数名称,默认为“格式”: #spring.mvc.contentnegotiation.parameter-name=myparam #我们还可以通过以下方式注册其他文件扩展名/媒体类型: spring.mvc.contentnegotiation.media-types.markdown=text/markdown 如果您了解注意事项并仍然希望应用程序使用后缀模式匹配,则需要进行以下配置: spring.mvc.contentnegotiation.favor-path-extension=true #您也可以将该功能限制为已知扩展 #spring.mvc.pathmatch.use-registered-suffix-pattern=true #我们还可以通过以下方式注册其他文件扩展名/媒体类型: #spring.mvc.contentnegotiation.media-types.adoc=text/asciidoc ConfigurableWebBindingInitializer Spring MVC使用WebBindingInitializer为每个特殊的请求初始化相应的WebDataBinder,如果你创建自己的ConfigurableWebBindingInitializer @Bean,Spring Boot会自动配置Spring MVC使用它。 模板引擎 正如REST web服务,你也可以使用Spring MVC提供动态HTML内容。Spring MVC支持各种各样的模板技术,包括Velocity, FreeMarker和JSPs,很多其他的模板引擎也提供它们自己的Spring MVC集成。 Spring Boot为以下的模板引擎提供自动配置支持: FreeMarker Groovy Thymeleaf Velocity(1.4已不再支持) Mustache 注:由于在内嵌servlet容器中使用JSPs存在一些已知的限制,所以建议尽量不使用它们。 使用以上引擎中的任何一种,并采用默认配置,则模块会从src/main/resources/templates自动加载。 注:IntelliJ IDEA根据你运行应用的方式会对classpath进行不同的排序。在IDE里通过main方法运行应用,跟从Maven,或Gradle,或打包好的jar中运行相比会导致不同的顺序,这可能导致Spring Boot不能从classpath下成功地找到模板。如果遇到这个问题,你可以在IDE里重新对classpath进行排序,将模块的类和资源放到第一位。或者,你可以配置模块的前缀为classpath*:/templates/,这样会查找classpath下的所有模板目录。 错误处理 Spring Boot默认提供一个/error映射用来以合适的方式处理所有的错误,并将它注册为servlet容器中全局的 错误页面。对于机器客户端(相对于浏览器而言,浏览器偏重于人的行为),它会产生一个具有详细错误,HTTP状态,异常信息的JSON响应。对于浏览器客户端,它会产生一个白色标签样式(whitelabel)的错误视图,该视图将以HTML格式显示同样的数据(可以添加一个解析为'error'的View来自定义它)。为了完全替换默认的行为,你可以实现ErrorController,并注册一个该类型的bean定义,或简单地添加一个ErrorAttributes类型的bean以使用现存的机制,只是替换显示的内容。 注BasicErrorController可以作为自定义ErrorController的基类,如果你想添加对新context type的处理(默认处理text/html),这会很有帮助。你只需要继承BasicErrorController,添加一个public方法,并注解带有produces属性的@RequestMapping,然后创建该新类型的bean。 你也可以定义一个@ControllerAdvice去自定义某个特殊controller或exception类型的JSON文档: @ControllerAdvice(basePackageClasses=FooController.class) publicclassFooControllerAdviceextendsResponseEntityExceptionHandler{ @ExceptionHandler(YourException.class) @ResponseBody ResponseEntity<?>handleControllerException(HttpServletRequestrequest,Throwableex){ HttpStatusstatus=getStatus(request); returnnewResponseEntity<>(newCustomErrorType(status.value(),ex.getMessage()),status); } privateHttpStatusgetStatus(HttpServletRequestrequest){ IntegerstatusCode=(Integer)request.getAttribute("javax.servlet.error.status_code"); if(statusCode==null){ returnHttpStatus.INTERNAL_SERVER_ERROR; } returnHttpStatus.valueOf(statusCode); } } 在以上示例中,如果跟FooController相同package的某个controller抛出YourException,一个CustomerErrorType类型的POJO的json展示将代替ErrorAttributes展示。 自定义错误页面 如果想为某个给定的状态码展示一个自定义的HTML错误页面,你需要将文件添加到/error文件夹下。错误页面既可以是静态HTML(比如,任何静态资源文件夹下添加的),也可以是使用模板构建的,文件名必须是明确的状态码或一系列标签。 例如,映射404到一个静态HTML文件,你的目录结构可能如下: src/ +-main/ +-java/ |+<sourcecode> +-resources/ +-public/ +-error/ |+-404.html +-<otherpublicassets> 使用FreeMarker模板映射所有5xx错误,你需要如下的目录结构: src/ +-main/ +-java/ |+<sourcecode> +-resources/ +-templates/ +-error/ |+-5xx.ftl +-<othertemplates> 对于更复杂的映射,你可以添加实现ErrorViewResolver接口的beans: publicclassMyErrorViewResolverimplementsErrorViewResolver{ @Override publicModelAndViewresolveErrorView(HttpServletRequestrequest, HttpStatusstatus,Map<String,Object>model){ //UsetherequestorstatustooptionallyreturnaModelAndView return... } } 你也可以使用Spring MVC特性,比如@ExceptionHandler方法和@ControllerAdvice,ErrorController将处理所有未处理的异常。 映射Spring MVC以外的错误页面 对于不使用Spring MVC的应用,你可以通过ErrorPageRegistrar接口直接注册ErrorPages。该抽象直接工作于底层内嵌servlet容器,即使你没有Spring MVC的DispatcherServlet,它们仍旧可以工作。 @Bean publicErrorPageRegistrarerrorPageRegistrar(){ returnnewMyErrorPageRegistrar(); } //... privatestaticclassMyErrorPageRegistrarimplementsErrorPageRegistrar{ @Override publicvoidregisterErrorPages(ErrorPageRegistryregistry){ registry.addErrorPages(newErrorPage(HttpStatus.BAD_REQUEST,"/400")); } } 注.如果你注册一个ErrorPage,该页面需要被一个Filter处理(在一些非Spring web框架中很常见,比如Jersey,Wicket),那么该Filter需要明确注册为一个ERROR分发器(dispatcher),例如: @Bean publicFilterRegistrationBeanmyFilter(){ FilterRegistrationBeanregistration=newFilterRegistrationBean(); registration.setFilter(newMyFilter()); ... registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class)); returnregistration; } (默认的FilterRegistrationBean不包含ERRORdispatcher类型)。 WebSphere应用服务器的错误处理 当部署到一个servlet容器时,Spring Boot通过它的错误页面过滤器将带有错误状态的请求转发到恰当的错误页面。request只有在response还没提交时才能转发(forwarded)到正确的错误页面,而WebSphere应用服务器8.0及后续版本默认情况会在servlet方法成功执行后提交response,你需要设置com.ibm.ws.webcontainer.invokeFlushAfterService属性为false来关闭该行为。 Spring HATEOAS 如果正在开发基于超媒体的RESTful API,你可能需要Spring HATEOAS,而Spring Boot会为其提供自动配置,这在大多数应用中都运作良好。 自动配置取代了@EnableHypermediaSupport,只需注册一定数量的beans就能轻松构建基于超媒体的应用,这些beans包括LinkDiscoverers(客户端支持),ObjectMapper(用于将响应编排为想要的形式)。ObjectMapper可以根据spring.jackson.*属性或Jackson2ObjectMapperBuilderbean进行自定义。 通过注解@EnableHypermediaSupport,你可以控制Spring HATEOAS的配置,但这会禁用上述ObjectMapper的自定义功能。 CORS支持 跨域资源共享(CORS)是一个大多数浏览器都实现了的W3C标准,它允许你以灵活的方式指定跨域请求如何被授权,而不是采用那些不安全,性能低的方式,比如IFRAME或JSONP。 从4.2版本开始,Spring MVC对CORS提供开箱即用的支持。不用添加任何特殊配置,只需要在Spring Boot应用的controller方法上注解@CrossOrigin,并添加CORS配置。通过注册一个自定义addCorsMappings(CorsRegistry)方法的WebMvcConfigurerbean可以指定全局CORS配置: @Configuration publicclassMyConfiguration{ @Bean publicWebMvcConfigurercorsConfigurer(){ returnnewWebMvcConfigurerAdapter(){ @Override publicvoidaddCorsMappings(CorsRegistryregistry){ registry.addMapping("/api/**"); } }; } } 本文相关视频连接

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

通用智能体的应用和最佳实践

现如今,开发者普遍已能借助 Codex、Claude Code、Cursor 这类编程助手来写代码。但写代码只是第一步,业务侧的需求会不断加码——比如自动抓取券商研报并做对比,这类实践已有不少人尝试;而让客服系统真正理解客户意图,难度又上了一个台阶。更进一步,有人已经把从数据采集到报告生成的整条流水线做成了无人值守的自动化流程,基于目标驱动的业务也随之升级。

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

ASCSDK-------通用包接入文档(COCOS篇)

先说下大体的思路。和之前说的在Unity中接入ASCSDK一样,在Cocos2dx中接入ASCSDK,我们也分为两步,一个Android中的部分,另 一个是Coccos2dx中的部分。同时,我们希望尽可能地封装不同平台之间数据的传递,以及接口的调用。使得大部分游戏基于这个封装,都可以最快地速度来完成ASCSDK的接入。 新建一个Android工程,作为ASCSDK和Cocos2dx的一个中间工程(ASCSDK_Cocos2dx),主要负责ASCSDK的接口调用封装,参数传递和解析等。这个工程,对于所有游戏都试用。 在Cocos2dx中封装JNI调用,以及和Android中交互的数据格式等的定义和解析。同样地,这部分对于所有游戏也都适用。游戏中只需要调用这里提供的相关接口,以及实现相应的回调即可完成接入。 首先, 我们使用cocos new 新建一个Cocos2dx(我这里使用的是Cocos2dx 3.10版本)工程,名为Cocos_Demo。 目录如下图 demo中我们创建了5个按钮,分别为登陆,支付,评价,展示横幅,展示插屏。代码以及运行效果如下。 #include "HelloWorldScene.h" USING_NS_CC; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); auto bg = Sprite::create("HelloWorld.jpg"); bg->setAnchorPoint(Vec2(0.5f, 0.5f)); bg->setPosition(Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2)); bg->setScale(2.0f); this->addChild(bg); ///////////////////////////// // 2. add a menu item with "X" image, which is clicked to quit the program // you may modify it. // add a "close" icon to exit the progress. it's an autorelease object auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 - 5.0f , origin.y + closeItem->getContentSize().height/2 + 5.0f)); // create menu, it's an autorelease object auto menu = Menu::create(closeItem, NULL); menu->setPosition(Vec2::ZERO); this->addChild(menu, 1); ///////////////////////////// // 3. add your codes below... // add a label shows "Hello World" // create and initialize a label this->labelUsername = Label::createWithTTF("ASCSDK", "fonts/Marker Felt.ttf", 24); // position the label on the center of the screen this->labelUsername->setPosition(Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height * 0.9 - this->labelUsername->getContentSize().height)); // add the label as a child to this layer this->addChild(this->labelUsername, 1); ui::Button* btnLogin = ui::Button::create("login_normal.png", "login_selected.png"); btnLogin->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y + 200)); btnLogin->addClickEventListener(CC_CALLBACK_1(HelloWorld::loginClickCallback, this)); btnLogin->setScale(0.6f); // add the sprite as a child to this layer this->addChild(btnLogin, 0); ui::Button* btnPay = ui::Button::create("pay_normal.png", "pay_selected.png"); btnPay->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y + 100)); btnPay->addClickEventListener(CC_CALLBACK_1(HelloWorld::payClickCallback, this)); btnPay->setScale(0.6f); // add the sprite as a child to this layer this->addChild(btnPay, 0); ui::Button* btnRate = ui::Button::create("rate_normal.png", "rate_selected.png"); btnRate->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y)); btnRate->addClickEventListener(CC_CALLBACK_1(HelloWorld::rateClickCallback, this)); btnRate->setScale(0.6f); // add the sprite as a child to this layer this->addChild(btnRate, 0); ui::Button* btnBanner = ui::Button::create("banner_normal.png", "banner_selected.png"); btnBanner->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y - 100)); btnBanner->addClickEventListener(CC_CALLBACK_1(HelloWorld::showBannerClickCallback, this)); btnBanner->setScale(0.6f); // add the sprite as a child to this layer this->addChild(btnBanner, 0); ui::Button* btnInters = ui::Button::create("inters_normal.png", "inters_selected.png"); btnInters->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y - 200)); btnInters->addClickEventListener(CC_CALLBACK_1(HelloWorld::showIntersClickCallback, this)); btnInters->setScale(0.6f); // add the sprite as a child to this layer this->addChild(btnInters, 0); //NDKHelper::addSelector("ASCSelectors", "onASCLoginResult", CC_CALLBACK_2(HelloWorld::onASCLoginResult, this), this); ASCSDKInterface::getInstance()->initSDK(this); return true; } void HelloWorld::OnInitSuc() { CCLOG("SDK初始化成功..."); //一般不需要做什么处理 } void HelloWorld::OnLoginSuc(ASCLoginResult* result) { CCLOG("SDK登录并认证成功...%s",result->userId.c_str()); this->labelUsername->setString(result->sdkUsername); if (result->isSwitchAccount) { //TODO:这里退回到登录界面,并且SDK是已经登录成功状态,不用再弹出SDK的登录界面 } else { //TODO:登录认证成功,获取服务器列表,进入游戏服... } } void HelloWorld::OnSwitchLogin() { CCLOG("切换帐号成功..."); //TODO:退回到登录界面,并弹出SDK登录界面(调用ASCSDKInterface::getInstance()->login()) } void HelloWorld::OnLogout() { CCLOG("SDK帐号退出成功..."); this->labelUsername->setString("ASCSDK"); //TODO:退回到登录界面,并弹出SDK登录界面(调用ASCSDKInterface::getInstance()->login()) } void HelloWorld::loginClickCallback(Ref *pSender) { CCLOG("Btn Login Clicked...ASC Login..."); ASCSDKInterface::getInstance()->login(); } void HelloWorld::payClickCallback(Ref *pSender) { CCLOG("Btn Pay Clicked...ASC Pay..."); ASCPayParams* params = ASCPayParams::create(); params->productId = "1"; params->productName = "元宝"; params->productDesc = "购买100元宝,赠送20元宝"; params->price = 100; params->buyNum = 1; params->coinNum = 300; params->serverId = "10"; params->serverName = "地狱之恋"; params->roleId = "asc_24532452"; params->roleName = "麻利麻利吼"; params->roleLevel = 15; params->vip = "v15"; //去服务器获取游戏中商品的订单号以及扩展数据,这里测试 params->orderID = "345435634534"; params->extension = "test111"; ASCSDKInterface::getInstance()->pay(params); } void HelloWorld::rateClickCallback(cocos2d::Ref* pSender){ CCLOG("Btn rate Clicked...ASC Rate..."); ASCSDKInterface::getInstance()->rate(); } void HelloWorld::showBannerClickCallback(cocos2d::Ref* pSender){ CCLOG("Btn showBanner Clicked...ASC showBanner..."); ASCSDKInterface::getInstance()->showBanner(); } void HelloWorld::showIntersClickCallback(cocos2d::Ref* pSender){ CCLOG("Btn showInters Clicked...ASC showInters..."); ASCSDKInterface::getInstance()->showInters(); } void HelloWorld::menuCloseCallback(Ref* pSender) { Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif } 说明: HelloWorldScene.cpp中init方法的最后,我们调用ASCSDK的初始化方法:ASCSDKInterface::getInstance()->initSDK(this); 实现回调接口 void HelloWorld::OnInitSuc() { CCLOG("SDK初始化成功..."); //一般不需要做什么处理 } void HelloWorld::OnLoginSuc(ASCLoginResult* result) { CCLOG("SDK登录并认证成功...%s",result->userId.c_str()); this->labelUsername->setString(result->sdkUsername); if (result->isSwitchAccount) { //TODO:这里退回到登录界面,并且SDK是已经登录成功状态,不用再弹出SDK的登录界面 } else { //TODO:登录认证成功,获取服务器列表,进入游戏服... } } void HelloWorld::OnSwitchLogin() { CCLOG("切换帐号成功..."); //TODO:退回到登录界面,并弹出SDK登录界面(调用ASCSDKInterface::getInstance()->login()) } void HelloWorld::OnLogout() { CCLOG("SDK帐号退出成功..."); this->labelUsername->setString("ASCSDK"); //TODO:退回到登录界面,并弹出SDK登录界面(调用ASCSDKInterface::getInstance()->login()) } 登陆以及支付以及广告调用,回调方法,具体调用,请参考demo。 3.将Cocos_Demo生成proj.android导入到eclipse中,然后将ASCSDK3和ASCSDK_Cocos2dx工程生成的jar包(分别是ascsdk2.jar和ascsdk_cocos2dx.jar)拷贝到HelloCocos安卓工程的libs目录下以及一些外部jar。然后,将生成的 AppActivity.java,本来是继承Cocos2dxActivity的,改为继承ASCCocosActivity。同时,将AndroidManifest.xml中的application节点中,加上android:name=”com.ascsdk.ASCApplication” 目录如图 AndroidManifest.xml内容 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.asc.sdk.demo.cocos" android:versionCode="1" android:versionName="1.0" android:installLocation="auto"> <uses-sdk android:minSdkVersion="19"/> <uses-feature android:glEsVersion="0x00020000" /> <application android:label="@string/app_name" android:icon="@drawable/icon" android:name="com.asc.sdk.ASCApplication" > <!-- Tell Cocos2dxActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="cocos2dcpp"/> <activity android:name=".AppActivity" android:label="@string/app_name" android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:configChanges="orientation|keyboardHidden|screenSize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <supports-screens android:anyDensity="true" android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true"/> <uses-permission android:name="android.permission.INTERNET"/> </manifest> 最后,为了能够在Android中编译并运行,我们还需要将ASCSDK包含的这些Cpp文件,添加到Android.mk中,最终的文件如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d/external) $(call import-add-path,$(LOCAL_PATH)/../../cocos2d/cocos) LOCAL_MODULE := cocos2dcpp_shared LOCAL_MODULE_FILENAME := libcocos2dcpp LOCAL_SRC_FILES := hellocpp/main.cpp \ ../../Classes/AppDelegate.cpp \ ../../Classes/HelloWorldScene.cpp \ ../../Classes/jansson/value.c \ ../../Classes/jansson/utf.c \ ../../Classes/jansson/strconv.c \ ../../Classes/jansson/strbuffer.c \ ../../Classes/jansson/pack_unpack.c \ ../../Classes/jansson/memory.c \ ../../Classes/jansson/load.c \ ../../Classes/jansson/hashtable.c \ ../../Classes/jansson/error.c \ ../../Classes/jansson/dump.c \ ../../Classes/NDKHelper/CallFuncNV.cpp \ ../../Classes/NDKHelper/NDKCallbackNode.cpp \ ../../Classes/NDKHelper/NDKHelper.cpp \ ../../Classes/ascsdk/ASCSDKData.cpp \ ../../Classes/ascsdk/ASCSDKNativeCallback.cpp \ ../../Classes/ascsdk/ASCSDKInterface.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes # _COCOS_HEADER_ANDROID_BEGIN # _COCOS_HEADER_ANDROID_END LOCAL_STATIC_LIBRARIES := cocos2dx_static # _COCOS_LIB_ANDROID_BEGIN # _COCOS_LIB_ANDROID_END include $(BUILD_SHARED_LIBRARY) $(call import-module,.) # _COCOS_LIB_IMPORT_ANDROID_BEGIN # _COCOS_LIB_IMPORT_ANDROID_END 4.到这里,接入就算完成了。我们可以使用cocos compile -p android 命令来编译Android平台,之前的操作如果稍有不慎,这里编译的过程中,也许会遇到一些错误,不过,看着啥错误,一一解决即可。

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

ASCSDK-------通用包接入文档(UNITY篇)

1. Untiy接入文档------Android端 Android端的作用是对接ASCSDK打包工具与unity原生端的中间件。 ASCUnityContext是主要的连接类,这个类继承UnityPlayerActivity ,也是游戏的启动Activity。 package com.asc.sdk; import java.util.List; import org.json.JSONException; import org.json.JSONObject; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.asc.sdk.platform.ASCExitListener; import com.asc.sdk.platform.ASCInitListener; import com.asc.sdk.platform.ASCPlatform; import com.asc.sdk.plugin.ASCUser; import com.asc.sdk.verify.UToken; import com.unity3d.player.UnityPlayer; import com.unity3d.player.UnityPlayerActivity; /*** * 记得将游戏工程中的AndroidManifest.xml中application节点,增加一个android:name="ASCApplication" * 如果游戏有自己的Application。那么通过实现IApplicationListener接口来实现,而不要使用继承Application。 * 然后将自己的Application类,配置到AndroidManifest.xml中的meta-data节点中,name为"ASC_Game_Application" * @author xiaohei * */ public class ASCUnityContext extends UnityPlayerActivity{ public final static String CALLBACK_GAMEOBJECT_NAME = "(ascsdk_callback)"; //unity中接收回调通知的GameObject的名称 public final static String CALLBACK_INIT = "OnInitSuc"; //SDK初始化成功的回调方法名称和Unity中一致 public final static String CALLBACK_LOGIN = "OnLoginSuc"; //SDK登录成功的回调方法名称和Unity中一致 public final static String CALLBACK_SWITCH_LOGIN = "OnSwitchLogin"; //SDK切换帐号的回调方法名称和Unity中一致 public final static String CALLBACK_LOGOUT = "OnLogout"; //SDK登出的回调方法名称和Unity中一致 public final static String CALLBACK_PAY = "OnPaySuc"; //SDK支付成功回调方法名称和Unity中一致 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initSDK(); } private void initSDK(){ ASCPlatform.getInstance().init(this, new ASCInitListener() { @Override public void onSwitchAccount(UToken data) { //游戏中通过SDK切换到新账号的回调,游戏收到该回调,需要引导用户重新登录,重新加载该新用户对应的角色数据 if(data == null){ tip("切换帐号失败,请重试"); return; } sendLoginResult(data, true); } @Override public void onPayResult(int code, String msg) { Log.d("ASCSDK", "pay result. code:"+code+";msg:"+msg); /*switch(code){ case ASCCode.CODE_PAY_SUCCESS: //tip("支付成功"); break; case ASCCode.CODE_PAY_FAIL: //tip("支付失败"); break; case ASCCode.CODE_PAY_CANCEL: //tip("支付取消"); break; case ASCCode.CODE_PAY_UNKNOWN: //tip("支付失败,未知错误"); break; }*/ } @Override public void onLogout() { //用户登出回调(需要收到该回调需要返回游戏登录界面,并调用login接口,打开SDK登录界面) sendCallback(ASCUnityContext.CALLBACK_LOGOUT, null); } @Override public void onLoginResult(int code, UToken data) { switch(code){ case ASCCode.CODE_LOGIN_SUCCESS: sendLoginResult(data, false); tip("登录成功"); break; case ASCCode.CODE_LOGIN_FAIL: tip("登录失败"); break; } } @Override public void onInitResult(int code, String msg) { Log.d("ASCSDK", "init result.code:"+code+";msg:"+msg); switch(code){ case ASCCode.CODE_INIT_SUCCESS: sendCallback(CALLBACK_INIT, null); tip("初始化成功"); break; case ASCCode.CODE_INIT_FAIL: tip("初始化失败"); break; } } @Override public void onProductQueryResult(List<ProductQueryResult> result) { // TODO Auto-generated method stub } @Override public void onVideoResult(int code, String msg) { // TODO Auto-generated method stub Log.d("ASCSDK", "onVideoResult result:"+msg); Toast.makeText(ASCUnityContext.this, "视频回调成功", Toast.LENGTH_LONG).show(); } @Override public void onSinglePayResult(int code, ASCOrder order) { Log.d("ASCSDK", "onSinglePayResult result:"+order.getProductID()); JSONObject json = new JSONObject(); String pId = ""; pId = order.getProductID(); try{ if(code == ASCCode.CODE_PAY_SUCCESS){ json.put("payResult", 0); }else{ json.put("payResult", 1); } json.put("productId", pId); }catch(Exception e){ e.printStackTrace(); } sendCallback(ASCUnityContext.CALLBACK_PAY,json.toString()); } @Override public void onDestroy() { // TODO Auto-generated method stub } }); } public void sendLoginResult(UToken authResult, boolean isSwitchAccount) { JSONObject json = new JSONObject(); try{ json.put("isSuc", authResult.isSuc()); json.put("isSwitchAccount", isSwitchAccount); if(authResult.isSuc()){ json.put("userID", authResult.getUserID()); json.put("sdkUserID", authResult.getSdkUserID()); json.put("username", authResult.getUsername()); json.put("sdkUsername", authResult.getSdkUsername()); json.put("token", authResult.getToken()); } }catch(Exception e){ e.printStackTrace(); } this.sendCallback(ASCUnityContext.CALLBACK_LOGIN, json.toString()); } public void nativeLog(String msg){ Log.d("ASC_LOG_UNITY", msg); } //登录接口 public void login(){ ASCPlatform.getInstance().login(this); } //显示用户中心接口 public void showAccountCenter(){ ASCPlatform.getInstance().showAccountCenter(); } //登出 public void logout(){ ASCPlatform.getInstance().logout(); } //提交扩展数据 public void submitExtraData(String data){ final UserExtraData extraData = parseGameData(data); ASCPlatform.getInstance().submitExtraData(extraData); } //SDK退出接口 public void exit(){ ASCPlatform.getInstance().exitSDK(new ASCExitListener() { @Override public void onGameExit() { //游戏自己的退出确认框 AlertDialog.Builder builder = new AlertDialog.Builder(ASCUnityContext.this); builder.setTitle("退出确认"); builder.setMessage("现在还早,要不要再玩一会?"); builder.setCancelable(true); builder.setPositiveButton("好吧", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { //这里什么都不用做 } }); builder.setNeutralButton("一会再玩", new DialogInterface.OnClickListener() { @SuppressLint("NewApi") public void onClick(DialogInterface dialog, int whichButton) { //退出游戏 ASCUnityContext.this.finish(); System.exit(0); } }); builder.show(); } }); } //支付接口 public void pay(String data){ final PayParams params = parsePayParams(data); ASCPlatform.getInstance().pay(this, params); } //SDK是否支持用户中心 public boolean isSupportAccountCenter(){ return ASCUser.getInstance().isSupport("showAccountCenter"); } //SDK是否支持退出确认框 public boolean isSupportExit(){ return ASCUser.getInstance().isSupport("exit"); } //SDK是否支持退出确认框 public boolean isSupportLogout(){ return ASCUser.getInstance().isSupport("logout"); } public boolean isSDKShowSplash(){ return ASCSDK.getInstance().isSDKShowSplash(); } //向Unity中发送消息 public void sendCallback(String name, String jsonParams){ if(jsonParams == null){ jsonParams = ""; } UnityPlayer.UnitySendMessage(CALLBACK_GAMEOBJECT_NAME, name, jsonParams); } private UserExtraData parseGameData(String str){ UserExtraData data = new UserExtraData(); try { JSONObject json = new JSONObject(str); data.setDataType(json.getInt("dataType")); data.setRoleID(json.getString("roleID")); data.setRoleName(json.getString("roleName")); data.setRoleLevel(json.getString("roleLevel")); data.setServerID(json.getInt("serverID")); data.setServerName(json.getString("serverName")); data.setMoneyNum(json.getInt("moneyNum")); String roleCreateTime = json.getString("roleCreateTime"); String roleLevelUpTime = json.getString("roleLevelUpTime"); if(!TextUtils.isEmpty(roleCreateTime.trim())){ data.setRoleCreateTime(Long.valueOf(roleCreateTime.trim())); } if(!TextUtils.isEmpty(roleLevelUpTime.trim())){ data.setRoleLevelUpTime(Long.valueOf(roleLevelUpTime.trim())); } data.setVip(json.optString("vip", "0")); } catch (JSONException e) { e.printStackTrace(); } return data; } private PayParams parsePayParams(String str){ PayParams params = new PayParams(); try{ JSONObject json = new JSONObject(str); params.setProductId(json.getString("productId")); params.setProductName(json.getString("productName")); params.setProductDesc(json.getString("productDesc")); params.setPrice(json.getInt("price")); params.setRatio(0);//该字段废弃不用 params.setBuyNum(json.getInt("buyNum")); params.setCoinNum(json.getInt("coinNum")); params.setServerId(json.getString("serverId")); params.setServerName(json.getString("serverName")); params.setRoleId(json.getString("roleId")); params.setRoleName(json.getString("roleName")); params.setRoleLevel(json.getInt("roleLevel")); params.setPayNotifyUrl(json.getString("payNotifyUrl")); params.setVip(json.getString("vip")); params.setExtension(json.getString("extension")); }catch(Exception e){ e.printStackTrace(); } return params; } public void onActivityResult(int requestCode, int resultCode, Intent data){ ASCSDK.getInstance().onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data); } public void onStart(){ ASCSDK.getInstance().onStart(); super.onStart(); } public void onPause(){ ASCSDK.getInstance().onPause(); super.onPause(); } public void onResume(){ ASCSDK.getInstance().onResume(); super.onResume(); } public void onNewIntent(Intent newIntent){ ASCSDK.getInstance().onNewIntent(newIntent); super.onNewIntent(newIntent); } public void onStop(){ ASCSDK.getInstance().onStop(); super.onStop(); } public void onDestroy(){ ASCSDK.getInstance().onDestroy(); super.onDestroy(); } public void onRestart(){ ASCSDK.getInstance().onRestart(); super.onRestart(); } private void tip(final String tip){ ASCSDK.getInstance().runOnMainThread(new Runnable() { @Override public void run() { //Toast.makeText(ASCUnityContext.this, tip, Toast.LENGTH_SHORT).show(); } }); } private void OnRate(){ try { String str = "market://details?id=" + ASCSDK.getInstance().getApplication().getPackageName(); Log.d("ASCSDK", "OnRate url:"+str); Intent localIntent = new Intent(Intent.ACTION_VIEW); localIntent.setData(Uri.parse(str)); ASCSDK.getInstance().getContext().startActivity(localIntent); } catch (Exception e) { // 打开应用商店失败 可能是没有手机没有安装应用市场 e.printStackTrace(); Toast.makeText(ASCSDK.getInstance().getContext().getApplicationContext(), "打开应用商店失败", Toast.LENGTH_SHORT).show(); // 调用系统浏览器进入商城 String url = "http://app.mi.com/detail/163525?ref=search"; openLinkBySystem(url); } } private void openLinkBySystem(String url) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); ASCSDK.getInstance().getContext().startActivity(intent); } } 说明: 其中,CALLBACK_GAMEOBJECT_NAME就是Unity中这个负责接收回调通知的GameObject的名称 此类包含了初始化SDK,登陆,登出,退出,支付,提交扩展数据方法。 ASCInitListener此接口为所有回调的监听接口。 onPayResult------支付回调(网游专用接口)。 onLoginResult------登陆结果回调。 onInitResult------初始化结果回调。 onSinglePayResult------单机支付结果回调。 还有一些登出,查询订单,视频回调等,详情看代码注解。 2. Untiy接入文档------Unity端 拷贝上面Android端生成的ascsdk_unitylib.jar到unity工程libs目录中。 拷贝ASCSDK的抽象层工程中的bin目录下生成的ascsdk3.jar拷贝到libs目录中。 在Unity中怎么来完成Android中相关接口的调用以及封装。 我们定义一个ASCSDKInterface的抽象单例类: public abstract class ASCSDKInterface{ public delegate void LoginSucHandler(ASCLoginResult data); public delegate void LogoutHandler(); private static ASCSDKInterface _instance; public LoginSucHandler OnLoginSuc; public LogoutHandler OnLogout; public static ASCSDKInterface Instance { get { if (_instance == null) { #if UNITY_EDITOR || UNITY_STANDLONE _instance = new SDKInterfaceDefault(); #elif UNITY_ANDROID _instance = new SDKInterfaceAndroid(); #elif UNITY_IOS _instance = new SDKInterfaceIOS(); #endif } return _instance; } } //初始化 public abstract void Init(); //登录 public abstract void Login(); //自定义登录,用于腾讯应用宝,QQ登录,customData="QQ";微信登录,customData="WX" public abstract void LoginCustom(string customData); //切换帐号 public abstract void SwitchLogin(); //登出 public abstract bool Logout(); //显示个人中心 public abstract bool ShowAccountCenter(); //上传游戏数据 public abstract void SubmitGameData(ASCExtraGameData data); //调用SDK的退出确认框,返回false,说明SDK不支持退出确认框,游戏需要使用自己的退出确认框 public abstract bool SDKExit(); //调用SDK支付界面 public abstract void Pay(ASCPayParams data); //SDK是否支持退出确认框 public abstract bool IsSupportExit(); //SDK是否支持用户中心 public abstract bool IsSupportAccountCenter(); //SDK是否支持登出 public abstract bool IsSupportLogout(); //去ASCServer获取游戏订单号,这里逻辑是访问游戏服务器,然后游戏服务器去ASCServer获取订单号 //并返回 public ASCPayParams reqOrder(ASCPayParams data) { //TODO 去游戏服务器获取订单号 //测试 data.orderID = "345435634534"; data.extension = "test"; return data; } } 有了这个抽象类,对于各个平台,我们需要分别实现该抽象类中的接口,从而间接地调用平台中的方法。对于Android平台,我们定义一个SDKInterfaceAndroid类: public class SDKInterfaceAndroid : ASCSDKInterface { private AndroidJavaObject jo; public SDKInterfaceAndroid() { using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); } } private T SDKCall<T>(string method, params object[] param) { try { return jo.Call<T>(method, param); } catch (Exception e) { Debug.LogError(e); } return default(T); } private void SDKCall(string method, params object[] param) { try { jo.Call(method, param); } catch (Exception e) { Debug.LogError(e); } } //这里Android中,在onCreate中直接调用了initSDK,所以这里就不用调用了 public override void Init() { //SDKCall("initSDK"); } public override void Login() { SDKCall("login"); } public override void LoginCustom(string customData) { SDKCall("loginCustom", customData); } public override void SwitchLogin() { SDKCall("switchLogin"); } public override bool Logout() { if (!IsSupportLogout()) { return false; } SDKCall("logout"); return true; } public override bool ShowAccountCenter() { if (!IsSupportAccountCenter()) { return false; } SDKCall("showAccountCenter"); return true; } public override void SubmitGameData(ASCExtraGameData data) { string json = encodeGameData(data); SDKCall("submitExtraData", json); } public override bool SDKExit() { if (!IsSupportExit()) { //return false; } SDKCall("exit"); return true; } public override void Pay(ASCPayParams data) { string json = encodePayParams(data); SDKCall("pay", json); } public override bool IsSupportExit() { return SDKCall<bool>("isSupportExit"); } public override bool IsSupportAccountCenter() { return SDKCall<bool>("isSupportAccountCenter"); } public override bool IsSupportLogout() { return SDKCall<bool>("isSupportLogout"); } private string encodeGameData(ASCExtraGameData data) { Dictionary<string, object> map = new Dictionary<string, object>(); map.Add("dataType", data.dataType); map.Add("roleID", data.roleID); map.Add("roleName", data.roleName); map.Add("roleLevel", data.roleLevel); map.Add("serverID", data.serverID); map.Add("serverName", data.serverName); map.Add("moneyNum", data.moneyNum); return MiniJSON.Json.Serialize(map); } private string encodePayParams(ASCPayParams data) { Dictionary<string, object> map = new Dictionary<string, object>(); map.Add("productId", data.productId); map.Add("productName", data.productName); map.Add("productDesc", data.productDesc); map.Add("price", data.price); map.Add("buyNum", data.buyNum); map.Add("coinNum", data.coinNum); map.Add("serverId", data.serverId); map.Add("serverName", data.serverName); map.Add("roleId", data.roleId); map.Add("roleName", data.roleName); map.Add("roleLevel", data.roleLevel); map.Add("vip", data.vip); map.Add("orderID", data.orderID); map.Add("extension", data.extension); return MiniJSON.Json.Serialize(map); } } 接下来,我们就定义一个专门负责回调的脚本: public class ASCSDKCallback : MonoBehaviour { private static ASCSDKCallback _instance; private static object _lock = new object(); //初始化回调对象 public static ASCSDKCallback InitCallback() { UnityEngine.Debug.LogError("Callback->InitCallback"); lock (_lock) { if (_instance == null) { GameObject callback = GameObject.Find("(ascsdk_callback)"); if (callback == null) { callback = new GameObject("(ascsdk_callback)"); UnityEngine.Object.DontDestroyOnLoad(_instance); _instance = callback.AddComponent<ASCSDKCallback>(); } else { _instance = callback.GetComponent<ASCSDKCallback>(); } } return _instance; } } //初始化成功回调 public void OnInitSuc() { //一般不需要处理 UnityEngine.Debug.LogError("Callback->OnInitSuc"); } //登录成功回调 public void OnLoginSuc(string jsonData) { UnityEngine.Debug.LogError("Callback->OnLoginSuc"); ASCLoginResult data = parseLoginResult(jsonData); if (data == null) { UnityEngine.Debug.LogError("The data parse error." + jsonData); return; } if (ASCSDKInterface.Instance.OnLoginSuc != null) { ASCSDKInterface.Instance.OnLoginSuc.Invoke(data); } } //切换帐号回调 public void OnSwitchLogin() { UnityEngine.Debug.LogError("Callback->OnSwitchLogin"); if (ASCSDKInterface.Instance.OnLogout != null) { ASCSDKInterface.Instance.OnLogout.Invoke(); } } //登出回调 public void OnLogout() { UnityEngine.Debug.LogError("Callback->OnLogout"); if (ASCSDKInterface.Instance.OnLogout != null) { ASCSDKInterface.Instance.OnLogout.Invoke(); } } //支付回调,网游不需要实现该接口,该接口用于单机游戏 public void OnPaySuc(string jsonData) { //Nothing... } private ASCLoginResult parseLoginResult(string str) { object jsonParsed = MiniJSON.Json.Deserialize(str); if (jsonParsed != null) { Dictionary<string, object> jsonMap = jsonParsed as Dictionary<string, object>; ASCLoginResult data = new ASCLoginResult(); if (jsonMap.ContainsKey("isSuc")) { data.isSuc = bool.Parse(jsonMap["isSuc"].ToString()); } if (jsonMap.ContainsKey("isSwitchAccount")) { data.isSwitchAccount = bool.Parse(jsonMap["isSwitchAccount"].ToString()); } if (jsonMap.ContainsKey("userID")) { data.userID = jsonMap["userID"].ToString(); } if (jsonMap.ContainsKey("sdkUserID")) { data.sdkUserID = jsonMap["sdkUserID"].ToString(); } if (jsonMap.ContainsKey("username")) { data.username = jsonMap["username"].ToString(); } if (jsonMap.ContainsKey("sdkUsername")) { data.sdkUsername = jsonMap["sdkUsername"].ToString(); } if (jsonMap.ContainsKey("token")) { data.token = jsonMap["token"].ToString(); } return data; } return null; } } 到这里,ASCSDK在Unity中的封装基本就算完成了,接下来,我们就来测试下接口的调用。我们用UGUI建立一个建立一个简单的面板,然后包含一个登录按钮和支付按钮,然后通过绑定一个ClickObject.cs脚本来简体按钮的响应事件: public class ClickObject : MonoBehaviour { // Use this for initialization private Text txtState; void Start () { ASCSDKCallback.InitCallback(); GameObject loginObj = GameObject.Find("BtnLogin"); Button btnLogin = loginObj.GetComponent<Button>(); btnLogin.onClick.AddListener(delegate() { OnLoginClick(); }); GameObject payObj = GameObject.Find("BtnPay"); Button btnPay = payObj.GetComponent<Button>(); btnPay.onClick.AddListener(delegate() { OnPayClick(); }); GameObject stateObj = GameObject.Find("TxtState"); txtState = stateObj.GetComponent<Text>(); ASCSDKInterface.Instance.OnLoginSuc = delegate(ASCLoginResult result) { OnLoginSuc(result); }; ASCSDKInterface.Instance.OnLogout = delegate() { OnLogout(); }; } void Update() { if (Input.GetKeyUp(KeyCode.Escape)) { if (!ASCSDKInterface.Instance.SDKExit()) { //TODO 退出确认框 Application.Quit(); } } } void OnLoginSuc(ASCLoginResult result) { if (!result.isSuc) { txtState.text = "登录失败"; return; } if (result.isSwitchAccount) { txtState.text = "切换帐号成功:" + result.token; } else { txtState.text = "登录成功:" + result.token; } } void OnLogout() { txtState.text = "未登录"; } void OnLoginClick() { ASCSDKInterface.Instance.Login(); } void OnPayClick() { ASCPayParams data = new ASCPayParams(); data.productId = "1"; data.productName = "元宝"; data.productDesc = "购买100元宝,赠送20元宝"; data.price = 100; data.buyNum = 1; data.coinNum = 300; data.serverId = "10"; data.serverName = "地狱之恋"; data.roleId = "asc_24532452"; data.roleName = "麻利麻利吼"; data.roleLevel = 15; data.vip = "v15"; data = ASCSDKInterface.Instance.reqOrder(data); ASCSDKInterface.Instance.Pay(data); } } 最后说明:我们将提供unity的工程的demo,具体可以参考我们demo,此文档暂时没有提供广告接口的说明,原理与login支付一样,调用我给你们提供的方法就可以,具体以我们给的demo为准。有任何问题可以联系我们的商务人员。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

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

Rocky Linux

Rocky Linux

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

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册