有米 Android SDK 之 Unity3d 使用教程

有米 Android SDK 之 Unity3d 使用教程文档

一、前言


1、文档说明

此文档是为方便使用Unity3d引擎的开发者嵌入有米广告SDK而撰写的,由于边幅有限,本文档只详细只针对以下几种广告形式的嵌入使用

  • Youmi Android OfferWall SDK v5.1.0 积分墙
  • Youmi Android NormalAds SDK v5.1.0 插屏、Banner

更多有米广告的功能使用请查看 官方AndroidSDK文档 ,同时,有错漏之处,敬请各位开发者指正。

2、环境配置

本教程在以下环境中进行开发示例:

二、嵌入流程概述


  1. 前往 有米主站 申请应用Appid
  2. 创建Android项目 ,提供调用广告的java方法给Unity3d中的脚本调用
  3. 创建Unity3d项目 ,创建脚本调用上述Android项目所提供的调用广告java方法

三、创建Android项目


1、使用Eclipse创建Android项目

_images/u3d_create_project_0.png  _images/u3d_create_project_1.png

然后一直点击Next,最后点击Finish完成Android工程的创建。

2、导入jar包

将 Unity3d的Android插件jar (Unity3d安装目录/Editor/Data/PlaybackEngines/androidplayer/release/bin/classess.jar)以及 有米AndroidSDK的jar (压缩包下/libs/YoumiSdk***.jar) 复制到Android工程目录下的 libs 文件夹(如果没有就新建这个目录)下,然后将这两个jar添加到项目中

_images/u3d_add_jar.png

3、配置AndroidManifest.xml

下面贴出了详细的配置,开发者请酌情选择配置。

注意:

  1. 开发者复制下面代码时,请注意需要修改包名(下面代码第3行)为你 创建项目所用的包名
  2. 无积分所需组件的值同样需要开发者更换为自己定义的值
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.youmi.android.unity3d.demo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="17" />

    <!-- 有米必备权限配置 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <!-- 以下为可选权限 -->
    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <!-- 本次演示所用Activity,本ctivity为竖屏,注意:复制代码时,请注意名字更换 -->
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 配置unity3d的activity -->
        <activity android:name="com.unity3d.player.UnityPlayerActivity" >
        </activity>

        <!-- 允许unity3d将事件传到DalvikVM的转发机制 -->
        <meta-data
            android:name="unityplayer.ForwardNativeEventsToDalvik"
            android:value="true" />

        <!-- 有米必备组件配置 -->
        <activity
            android:name="net.youmi.android.AdBrowser"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:theme="@android:style/Theme.Light.NoTitleBar" >
        </activity>

        <service
            android:name="net.youmi.android.AdService"
            android:exported="false" >
        </service>

        <receiver android:name="net.youmi.android.AdReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />

                <data android:scheme="package" />
            </intent-filter>
        </receiver>

            <!-- 有米视频广告组件 -->
            <activity
            android:name="net.youmi.android.video.VideoActivity"
            android:configChanges="keyboard|keyboardHidden|screenSize|orientation"
            android:screenOrientation="landscape"
            android:theme="@android:style/Theme.NoTitleBar" >
            </activity>

        <!-- 有米积分广告所需组件 -->
        <service
            android:name="net.youmi.android.ExpService"
            android:exported="false" >
        </service>

        <!-- (可选)设置有米广告推广渠道号,参数列表:http://wiki.youmi.net/Wiki/PromotionChannelIDs -->
        <meta-data
            android:name="YOUMI_CHANNEL"
            android:value="这里替换为非负整数的渠道号" >
        </meta-data>
    </application>

</manifest>

4、编写广告代码

为了方便起见,下面贴出了广告使用的简明代码,开发者可以酌情复制。

注意:

开发者复制代码后需要替换appid 和appsecret 为 自己在 有米主站 上面申请的appid和appsecret

package com.youmi.android.unity3d.demo;

import net.youmi.android.AdManager;
import net.youmi.android.banner.AdSize;
import net.youmi.android.banner.AdView;
import net.youmi.android.banner.AdViewListener;
import net.youmi.android.listener.Interface_ActivityListener;
import net.youmi.android.offers.OffersManager;
import net.youmi.android.offers.OffersWallDialogListener;
import net.youmi.android.offers.PointsChangeNotify;
import net.youmi.android.offers.PointsManager;
import net.youmi.android.spot.SpotDialogListener;
import net.youmi.android.spot.SpotManager;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity implements PointsChangeNotify {

    /**
     * 无积分Banner
     */
    AdView mAdView;

    /**
     * Handler,用于线程与UI交互
     */
    Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler();

        // 初始化接口,应用启动的时候调用,参数:appId, appSecret, 是否开启调试模式
        AdManager.getInstance(this).init("9b26c34dca959658", "f5821b6374083845", false);

        // (可选)开启用户数据统计服务,(sdk v4.08之后新增功能)默认不开启,传入false值也不开启,只有传入true才会调用
        AdManager.getInstance(this).setUserDataCollect(true);

        // --------------------------------------------------------------------------------
        // 积分广告初始化及相关设置
        // 如果使用积分广告,请务必调用积分广告的初始化接口:
        OffersManager.getInstance(this).onAppLaunch();

        // (可选)注册积分监听-随时随地获得积分的变动情况
        PointsManager.getInstance(this).registerNotify(this);

        // --------------------------------------------------------------------------------
        // 插屏接口初始化及相关设置
        // (建议使用)预加载插播资源
        SpotManager.getInstance(this).loadSpotAds();

        // (可选) 设置插屏图片为竖屏
        SpotManager.getInstance(this).setSpotOrientation(
                SpotManager.ORIENTATION_PORTRAIT);

        // (可选) 设置插屏广告动画效果为高级动画效果
        SpotManager.getInstance(this).setAnimationType(SpotManager.ANIM_ADVANCE);

                    // --------------------------------------------------------------------------------
                    // 视频广告接口初始化及相关设置
                    // (建议使用)预加载视频数据
                    // 视频设置,提供视频的各种设置,如设置退出提示语,更改退出按钮,加载中的logo。
                    VideoAdManager.getInstance(this).getVideoAdSetting().setInterruptsTips("是否要退出视频");

                    VideoAdManager.getInstance(this).requestVideoAd(new VideoAdRequestListener() {

                            @Override
                            public void onRequestSucceed() {
                                    Log.d("videoPlay", "请求成功");
                            }

                            @Override
                            public void onRequestFail(int errorCode) {
                                    // 关于错误码的解读:-1为网络连接失败,请检查网络。-2007为无广告,-3312为该设备一天的播放次数已完,其他错误码一般为设备问题。
                                    Log.d("videoPlay", "请求失败,错误码为:" + errorCode);
                            }
                    });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // (可选)注销积分监听
        // 如果在onCreate调用了PointsManager.getInstance(this).registerNotify(this)进行积分余额监听器注册,那这里必须得注销
        PointsManager.getInstance(this).unRegisterNotify(this);

        // 回收积分广告占用的资源
        OffersManager.getInstance(this).onAppExit();

        // 回收插屏广告占用的资源
        SpotManager.getInstance(this).onDestroy();
        super.onDestroy();
    }


            public void showVideo() {
                    mHandler.post(new Runnable() {

                            @Override
                            public void run() {
                                    VideoAdManager.getInstance(MainActivity.this).showVideo(MainActivity.this,
                                                    new VideoAdListener() {

                                                            // 视频播放失败
                                                            @Override
                                                            public void onVideoPlayFail() {
                                                                    Log.d("videoPlay", "failed");
                                                            }

                                                            // 视频播放完成
                                                            @Override
                                                            public void onVideoPlayComplete() {
                                                                    Log.d("videoPlay", "complete");
                                                                    Toast.makeText(MainActivity.this, "您获得了1个金币的奖励", Toast.LENGTH_SHORT).show();
                                                            }

                                                            // 视频播放完成的记录向服务器发送是否成功
                                                            @Override
                                                            public void onVideoCallback(boolean callback) {
                                                                    // 视屏播放记录发送是否回调成功
                                                                    Log.d("videoPlay", "completeEffect:" + callback);
                                                            }

                                                            // 视频播放中途退出
                                                            @Override
                                                            public void onVideoPlayInterrupt() {
                                                                    Log.d("videoPlay", "interrupt");
                                                                    Toast.makeText(MainActivity.this, "视频未播放完成,无法获取奖励", Toast.LENGTH_SHORT).show();
                                                            }

                                                            @Override
                                                            public void onDownloadComplete(String id) {

                                                            }

                                                            @Override
                                                            public void onNewApkDownloadStart() {

                                                            }

                                                            @Override
                                                            public void onVideoLoadComplete() {
                                                                    // TODO Auto-generated method stub

                                                            }

                                                    });
                            }
                    });
            }



    /**
     * 调用无积分插播广告--可以在Unity3d中直接调用
     */
    public void showSpot() {
        mHandler.post(new Runnable() {

            @Override
            public void run() {
                // 展示插屏广告,可以不调用loadSpot独立使用
                SpotManager.getInstance(MainActivity.this).showSpotAds(MainActivity.this,
                        new SpotDialogListener() {

                            @Override
                            public void onShowSuccess() {
                                showTipsInUiThread("插屏广告展示成功", Toast.LENGTH_SHORT);
                            }

                            @Override
                            public void onShowFailed() {
                                showTipsInUiThread("插屏广告展示失败\n原因请查看Logcat-tag:YoumiSdk", Toast.LENGTH_LONG);
                            }

                            @Override
                            public void onSpotClosed() {
                                showTipsInUiThread("插屏广告关闭了", Toast.LENGTH_SHORT);
                            }

                        });
            }
        });
    }

    /**
     * 如果插屏广告展示时,需要通过按返回键来进行关闭插屏广告的话,就调用下面这个方法(可选)
     *
     * @param type
     *            0 表示按了返回键 <br>
     *            1 标识按了home键
     * @return true 插屏广告已经消失了<br>
     *         false 插屏广告还没有消失
     */
    public boolean closeSpot(int type) {

        if (type == 0) {
            if (!SpotManager.getInstance(this).disMiss()) {
                // 如果没有插屏广告在展示,就返回true,执行开发者的逻辑
                return true;
            } else {
                // 如果有插屏广告在展示,就返回false
                return false;
            }
        }
        if (type == 1) {
            // 按home键时,调用尝试关闭插屏广告的代码
            // 如果不调用此方法,则按home键的时候会出现图标无法显示的情况。
            SpotManager.getInstance(this).onStop();
            return true;
        }
        return true;
    }

    /**
     * 实例化无积分Banner并且将其加入到游戏界面中 --可以在Unity3d中直接调用
     */
    public void showBanner() {
        if (mAdView == null) {
            mHandler.post(new Runnable() {

                @Override
                public void run() {
                    // 实例化广告条
                    mAdView = new AdView(MainActivity.this, AdSize.FIT_SCREEN);
                    mAdView.setAdListener(new AdViewListener() {

                        @Override
                        public void onSwitchedAd(AdView arg0) {
                            showTipsInUiThread("广告条切换广告了", Toast.LENGTH_SHORT);
                        }

                        @Override
                        public void onReceivedAd(AdView arg0) {
                            showTipsInUiThread("广告条接收到广告了", Toast.LENGTH_SHORT);
                        }

                        @Override
                        public void onFailedToReceivedAd(AdView arg0) {
                            showTipsInUiThread("广告条展示失败", Toast.LENGTH_LONG);
                        }
                    });
                    // 创建布局来承载广告条
                    LinearLayout layout = new LinearLayout(MainActivity.this);
                    layout.addView(mAdView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
                            LinearLayout.LayoutParams.WRAP_CONTENT));

                    // 采用WindowManager来进行
                    WindowManager mWindowManager = (WindowManager) MainActivity.this
                            .getSystemService(Context.WINDOW_SERVICE);
                    WindowManager.LayoutParams mWmParams = new WindowManager.LayoutParams();
                    mWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                    mWmParams.height = -2;
                    mWmParams.width = -1;
                    mWmParams.alpha = 1.0F;
                    mWmParams.format = 1;
                    mWmParams.gravity = Gravity.BOTTOM;
                    mWindowManager.addView(layout, mWmParams);

                }
            });
        }
    }

    /**
     * 展示全屏积分墙--可以在Unity3d中直接调用
     */
    public void showOffers() {
        mHandler.post(new Runnable() {

            @Override
            public void run() {
                OffersManager.getInstance(MainActivity.this).showOffersWall(new Interface_ActivityListener() {

                    @Override
                    public void onActivityDestroy(Context context) {
                        // TODO Auto-generated method stub
                        showTipsInUiThread("全屏积分墙退出了", Toast.LENGTH_LONG);
                    }
                });
            }
        });
    }

    /**
     * 展示对话框积分墙--可以在Unity3d中直接调用
     */
    public void showOffersDialog() {
        mHandler.post(new Runnable() {

            @Override
            public void run() {
                OffersManager.getInstance(MainActivity.this).showOffersWallDialog(MainActivity.this,
                        new OffersWallDialogListener() {

                            @Override
                            public void onDialogClose() {
                                showTipsInUiThread("积分墙对话框关闭了", Toast.LENGTH_LONG);
                            }
                        });
            }
        });
    }

    /**
     * 查询积分--可以在Unity3d中直接调用
     *
     * @return
     */
    public int queryPoints() {
        return PointsManager.getInstance(MainActivity.this).queryPoints();
    }

    /**
     * 消费积分--可以在Unity3d中直接调用
     *
     * @param p
     * @return
     */
    public boolean spendPoints(int p) {
        return PointsManager.getInstance(MainActivity.this).spendPoints(p);
    }

    /**
     * 奖励积分(如用户完成了某个你觉得需要奖励他积分的操作时调用此接口进行奖励)--可以在Unity3d中直接调用
     *
     * @param p
     * @return
     */
    public boolean awardPoints(int p) {
        return PointsManager.getInstance(MainActivity.this).awardPoints(p);
    }

    /**
     * 积分余额变动通知,单用户的积分发生变动(增加或者减少)时,会回调本方法,本方法执行在UI线程中
     */
    @Override
    public void onPointBalanceChange(int arg0) {
        // 当积分余额变动时,通知unity3d进行界面更新,
        // 参数1:发送游戏对象的名称
        // 参数2:对象绑定的脚本接收该消息的方法
        // 参数3:本条消息发送的字符串信息
        UnityPlayer.UnitySendMessage("Main Camera", "UpdatePoints", String.valueOf(arg0));
    }

    public void showTipsInUiThread(final String str, final int duartion) {
        mHandler.post(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(MainActivity.this, str, duartion).show();
            }
        });
    }
}

5、生成给Unity3d使用的jar

  1. 清理项目:依次点击菜单栏Project->Clean->选择项目->确定

  2. 重新构建项目:依次点击菜单栏Project->Build Project(如果此选项不可点击,请先取消自动构建项目的设置,即点击菜单栏Project->BuildAutomatically)

  3. 使用cmd等命令行工具,到达 Android工程项目根路径/bin/classes 目录下,运行命令 jar -cvf ..\youmiu3ddemo.jar * 生成给Unity3d项目使用的jar,生成的jar在 Android工程项目根路径/bin 目录下

    _images/u3d_create_jar.png
  4. 至此,Android工程项目的准备工作已完成。

四、创建Unity3d项目


1、配置Android SDK路径

开始之前请开发者自行设置Android Sdk的路径: 依次进入Edit->Preferences->External Tools->Android SDK Location

2、创建Unity3d for Android项目

  1. 新建项目
  2. 设置项目参数
_images/u3d_create_u3d_1.png  _images/u3d_create_u3d_2.png  _images/u3d_create_u3d_3.png

注意:

Unity3d项目中的默认屏幕方向,需要与Android项目中的 AndroidManifest.xml 中所设置的保持一致,不然启动app之后旋转屏幕可能会app崩溃的现象

_images/u3d_create_u3d_4.png

3、导入Android项目相关资源

需要导入的资源列表:

  • Android工程目录/res
  • Android工程目录/AndroidManifest.xml
  • Android工程目录/bin/youmiu3ddemo.jar,jar在上述 第五步 中生成
  • 有米AndroidSDK的jar
_images/u3d_import_android.png

4、编写脚本

在Assets目录下新建脚本,这里提供C#和Js两种脚本示例,任选一种即可:

C#脚本示例
using UnityEngine;
using System.Collections;

public class YoumiU3dDemo : MonoBehaviour {

    AndroidJavaClass mJc;
    AndroidJavaObject mJo;
    private int mPoints;

    // 更新积分,这个方法在Android项目中调用
    void UpdatePoints(string points) {
        this.mPoints = int.Parse(points);
    }

    void OnGUI(){
        GUILayout.Label("Youmi Unity3d Demo");
        GUILayout.Label("Current Points: "+mPoints);

        // 调用Android工程提供的api——展示插屏广告
        if(GUILayout.Button("Show Spot",GUILayout.Height(100))){
            mJo.Call("showSpot");
        }

                    // 调用Android工程提供的api——展示视频广告
                    if (GUILayout.Button ("Show Video", GUILayout.Height (150))) {
                            mJo.Call("showVideo");
                    }

        // 调用Android工程提供的api——展示全屏积分墙
        if(GUILayout.Button("Show Offers",GUILayout.Height(100))){
            mJo.Call("showOffers");
        }

        // 调用Android工程提供的api——展示对话框积分墙
        if(GUILayout.Button("Show Offers Dialog",GUILayout.Height(100))){
            mJo.Call("showOffersDialog");
        }

        // 调用Android工程提供的api——展查询积分
        if(GUILayout.Button("Query Points",GUILayout.Height(100))){
            this.mPoints=mJo.Call<int>("queryPoints");
        }

        // 调用Android工程提供的api——奖励10积分
        if(GUILayout.Button("Award 10 Points",GUILayout.Height(100))){
            if(mJo.Call<bool>("awardPoints",10)){
                this.mPoints=mJo.Call<int>("queryPoints");
            }
        }

        // 调用Android工程提供的api——消耗5积分
        if(GUILayout.Button("Spend 5 Points",GUILayout.Height(100))){
            if(mJo.Call<bool>("spendPoints",5)){
                this.mPoints=mJo.Call<int>("queryPoints");
            }
        }

        if(GUILayout.Button("Exit",GUILayout.Height(100))){
            Application.Quit();
        }
    }

    void Start () {
        mJc=new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        mJo=mJc.GetStatic<AndroidJavaObject>("currentActivity");
        mJo.Call("showBanner");
    }

    void Update () {
        if(Input.GetKeyDown(KeyCode.Escape)){
            // 如果开发者使用了插屏广告,那么当按返回键的时候,逻辑应该如下:
            // 1、如果插屏广告在展示时,返回键应该先关闭正在展示的插屏广告,在按一次返回键才执行开发者自己的逻辑(如:退出应用)
            // 2、如果插屏广告没有在展示时,就进行自己的逻辑(如:退出应用等)

            // 当插屏广告已经消失了,就执行后续逻辑(这里为退出应用)
            // Android示例项目中定义0为返回键
            if (mJo.Call<bool>("closeSpot", 0) == true) {
                Application.Quit();
            }
        }
        if(Input.GetKeyDown(KeyCode.Home)){
            // 按Home键时,调用尝试关闭插屏广告的代码,开发者可以实现后续逻辑
            // Android示例项目中定义1为Home键
            if (mJo.Call<bool>("closeSpot", 1) == true) {

            }
        }
    }
}
Js脚本示例
private var mPoints:int;
var mJc:AndroidJavaClass;
var mJo:AndroidJavaObject;

// 更新积分,这个方法在Android项目中调用
function UpdatePoints(points:String){
    mPoints=parseInt(points);
}

function OnGUI(){

    GUILayout.Label("Youmi Unity Demo");
    GUILayout.Label("Current Points:"+mPoints);

    // 调用Android工程提供的api——展示插屏广告
    if(GUILayout.Button("Show Spot",GUILayout.Height(100))) mJo.Call("showSpot");

    // 调用Android工程提供的api——展示全屏积分墙
    if(GUILayout.Button("Show Offers",GUILayout.Height(100))) mJo.Call("showOffers");

    // 调用Android工程提供的api——展示对话框积分墙
    if(GUILayout.Button("Show Offers Dialog",GUILayout.Height(100))) mJo.Call("showOffersDialog");

    // 调用Android工程提供的api——展查询积分
    if(GUILayout.Button("Query Points",GUILayout.Height(100))) mPoints=mJo.Call.<int>("queryPoints");

    // 调用Android工程提供的api——奖励10积分
    if(GUILayout.Button("Award 10 Points",GUILayout.Height(100)))
        if(mJo.Call.<boolean>("awardPoints",10))
            mPoints=mJo.Call.<int>("queryPoints");

    // 调用Android工程提供的api——消耗5积分
    if(GUILayout.Button("Spend 5 Points",GUILayout.Height(100)))
        if(mJo.Call.<boolean>("spendPoints",5))
            mPoints=mJo.Call.<int>("queryPoints");

    //退出
    if(GUILayout.Button("Exit",GUILayout.Height(100))) Application.Quit();
}

function Start () {
    mJc=new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    mJo=mJc.GetStatic.<AndroidJavaObject>("currentActivity");
    mJo.Call("showBanner");
}

function Update () {
    if(Input.GetKeyDown(KeyCode.Escape)){
        // 如果开发者使用了插屏广告,那么当按返回键的时候,逻辑应该如下:
        // 1、如果插屏广告在展示时,返回键应该先关闭正在展示的插屏广告,在按一次返回键才执行开发者自己的逻辑(如:退出应用)
        // 2、如果插屏广告没有在展示时,就进行自己的逻辑(如:退出应用等)

        // 当插屏广告已经消失了,就执行后续逻辑(这里为退出应用)
        // Android示例项目中定义0为返回键
        if (mJo.Call.<boolean>("closeSpot", 0)) {
            Application.Quit();
        }
    }
    if(Input.GetKeyDown(KeyCode.Home)){
        // 按Home键时,调用尝试关闭插屏广告的代码,开发者可以实现后续逻辑
        // Android示例项目中定义1为Home键
    if (mJo.Call.<boolean>("closeSpot", 1)) {

        }
    }
}

注意:

本示例中,demo生成了几个按钮,需要将脚本绑定到摄像机

_images/u3d_bundle_script.png

5、编译运行

至此,工作已经完毕,可以进行编译运行,此时建议连上手机进行调试

_images/u3d_buildrun.png

附:Demo运行界面

_images/u3d_demoshortcut.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值