主頁 > 後端開發 > 零基礎開啟元宇宙|如何快速創建虛擬形象

零基礎開啟元宇宙|如何快速創建虛擬形象

2022-12-13 07:03:34 後端開發

元宇宙(Metaverse),是人類運用數字技術構建的,由現實世界映射或超越現實世界,可與現實世界互動的虛擬世界,具備新型社會體系的數字生活空間,

可見元宇宙第一步是創建專屬虛擬形象,但創建3D虛擬形象需要3D基礎知識,對于大部分android開發者(包括我本人)來說沒有這方面的積累,難道因此我們就難以進入元宇宙的世界嗎?不,今天我們借助即構平臺提供的Avatar SDK,只要有Android基礎即可進入最火的元宇宙世界!先看效果:

請添加圖片描述

上面gif被壓縮的比較狠,這里放一張截圖:

請添加圖片描述

1 免費注冊即構開發者

前往即構控制臺網站:https://console.zego.im/注冊開發者賬戶,注冊成功后,創建專案:

請添加圖片描述

控制臺中可以得到AppID和AppSign兩個資料,這兩個資料是重要憑證,后面會用到,

由于我們用到了即構的Avatar功能,但目前官方沒有提供線上自動開啟方式,需要主動找客服申請(當然,這是免費的),只需提供自己專案的包名,即可開通Avatar權限,打開https://doc-zh.zego.im/article/15206右下角有“聯系我們”,點擊即可跟客服申請免費開通權限,

注意,如果不向客服申請Avatar權限,呼叫AvatarSDK會失敗!

2 準備開發環境

前往即構官方元宇宙開發SDK網站https://doc-zh.zego.im/article/15302下載SDK,得到如下檔案串列:

請添加圖片描述

接下來程序如下:

  1. 打開SDK目錄,將里面的ZegoAvatar.aar拷貝至app/libs目錄下,
  2. 添加SDK參考,打開app/build.gradle檔案,在dependencies節點引入 libs下所有的jaraar:

dependencies {
 
    implementation fileTree(dir: 'libs', include: ['*.jar', "*.aar"]) //通配引入
    
    //其他略
}
  1. 設定權限,根據實際應用需要,設定應用所需權限,進入app/src/main/AndroidManifest.xml 檔案,添加權限,
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-feature
    android:glEsVersion="0x00020000"
    android:required="true" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

因為Android 6.0在一些比較重要的權限上要求必須申請動態權限,不能只通過 AndroidMainfest.xml檔案申請靜態權限,具體動態請求權限代碼可看附件原始碼,

3 匯入資源

上一小節下載的zip檔案中,有個assets目錄,里面包含了Avatar形象相關資源,如:衣服、眉毛、鞋子等,這是即構官方免費提供的資源,可以滿足一般性需求了,當然了,如果想要自己定制資源也是可以的,assets檔案內容如下:

請添加圖片描述

資源名稱 說明
AIModel.bundle Avatar 的 AI 模型資源,當使用表情隨動、聲音隨動、AI 捏臉等能力時,必須先將該資源的絕對路徑設定給 Avatar SDK,
base.bundle 美術資源,包含基礎 3D 人物模型資源、資源映射表、人物模型默認外形等,
Packages 美妝、掛件、裝飾等資源, 每個資源 200 KB ~ 1 MB 不等,跟資源復雜度相關,

注意:由于資源檔案很大,上面下載的美術資源只包含少量必須資源,如果需要全部資源,可以去官網找客服索要:https://doc-zh.zego.im/article/14882

上面的檔案需要存放到Android本地SDCard上,這里有2個方案可供參考:

  • 方案一: 將資源先放入到app/src/assets目錄內,然后在app啟動時,自動將assets的內容拷貝到SDcard中,
  • 方案二: 將資源放入到服務器端,運行時自動從服務器端下載,

為了簡單起見,我們這里采用方案一,參考代碼如下, 詳細代碼可以看附件:

AssetsFileTransfer.copyAssetsDir2Phone(this.getApplication(),
            "AIModel.bundle", "assets");
AssetsFileTransfer.copyAssetsDir2Phone(this.getApplication(),
            "base.bundle", "assets");
AssetsFileTransfer.copyAssetsDir2Phone(this.getApplication(),
            "Packages", "assets");

4 創建虛擬形象

創建虛擬形象本質上來說就是呼叫即構的Avatar SDK,其大致流程如下:

請添加圖片描述

接下來我們逐步實作上面流程,

需要注意的是,上面示意圖中采用的是AvatarView,可以非常方便的直接展示Avatar形象,但是不方便后期將畫面通過RTC實時傳遞, 因此,我們后面的具體實作視通過TextureView替代AvatarView,

4.1 申請權鑒

這里再次強調一下,一定要打開https://doc-zh.zego.im/article/15206點擊右下角有“聯系我們”,向客服申請免費開通Avatar權限,否則無法使用Avatar SDK

申請權鑒代碼如下:


public class KeyCenter { 
    // 控制臺地址: https://console.zego.im/dashboard 
    public static long APP_ID = 這里值可以在控制臺查詢,參考第一節;  //這里填寫APPID
    public static String APP_SIGN =  這里值可以在控制臺查詢,參考第一節; 
    // 鑒權服務器的地址
    public final static String BACKEND_API_URL = "https://aieffects-api.zego.im?Action=DescribeAvatarLicense";

    public static String avatarLicense = null;
    public static String getURL(String authInfo) {
        Uri.Builder builder = Uri.parse(BACKEND_API_URL).buildUpon();
        builder.appendQueryParameter("AppId", String.valueOf(APP_ID));
        builder.appendQueryParameter("AuthInfo", authInfo);

        return builder.build().toString();
    }

    public interface IGetLicenseCallback {
        void onGetLicense(int code, String message, ZegoLicense license);
    }

    /**
     * 在線拉取 license
     * @param context
     * @param callback
     */
    public static void getLicense(Context context, final IGetLicenseCallback callback) {
        requestLicense(ZegoAvatarService.getAuthInfo(APP_SIGN, context), callback);
    }

    /**
     * 獲取license
     * */
    public static void requestLicense(String authInfo, final IGetLicenseCallback callback) {

        String url = getURL(authInfo);

        HttpRequest.asyncGet(url, ZegoLicense.class, (code, message, responseJsonBean) -> {
            if (callback != null) {
                callback.onGetLicense(code, message, responseJsonBean);
            }
        });
    }


    public class ZegoLicense {
        @SerializedName("License")
        private String license;
        public String getLicense() {
            return license;
        }
        public void setLicense(String license) {
            this.license = license;
        }
    }

}

在獲取Lincense時,只需呼叫getLicense函式,例如在Activity類中只需如下呼叫:

KeyCenter.getLicense(this, (code, message, response) -> {
        if (code == 0) {
            KeyCenter.avatarLicense = response.getLicense();
            showLoading("正在初始化...");
            avatarMngr = AvatarMngr.getInstance(getApplication());
            avatarMngr.setLicense(KeyCenter.avatarLicense, this);
        } else {
            toast("License 獲取失敗, code: " + code);
        }
    });

4.2 初始化AvatarService

初始化AvatarService程序比較漫長(可能要幾秒),通過開啟worker執行緒后臺加載以避免主執行緒阻塞,因此我們定義一個回呼函式,待完成初始化后回呼通知:


public interface OnAvatarServiceInitSucced {
    void onInitSucced();
}

public void setLicense(String license, OnAvatarServiceInitSucced listener) {
    this.listener = listener;
    ZegoAvatarService.addServiceObserver(this);
    String aiPath = FileUtils.getPhonePath(mApp, "AIModel.bundle", "assets"); //   AI 模型的絕對路徑
    ZegoServiceConfig config = new ZegoServiceConfig(license, aiPath);
    ZegoAvatarService.init(mApp, config);
}

@Override
public void onStateChange(ZegoAvatarServiceState state) {
    if (state == ZegoAvatarServiceState.InitSucceed) {
        Log.i("ZegoAvatar", "Init success");
        // 要記得及時移除通知
        ZegoAvatarService.removeServiceObserver(this);
        if (listener != null) listener.onInitSucced();
    }
}

這里setLicense函式內完成初始化AvatarService,初始化完成后會回呼onStateChange函式,但是要注意,在初始化之前必須把資源檔案拷貝到本地SDCard,即完成資源匯入:

private void initRes(Application app) {
    // 先把資源拷貝到SD卡,注意:線上使用時,需要做一下判斷,避免多次拷貝,資源也可以做成從網路下載,
    if (!FileUtils.checkFile(app, "AIModel.bundle", "assets"))
        FileUtils.copyAssetsDir2Phone(app, "AIModel.bundle", "assets");
    if (!FileUtils.checkFile(app, "base.bundle", "assets"))
        FileUtils.copyAssetsDir2Phone(app, "base.bundle", "assets");
    if (!FileUtils.checkFile(app, "human.bundle", "assets"))
        FileUtils.copyAssetsDir2Phone(app, "human.bundle", "assets");
    if (!FileUtils.checkFile(app, "Packages", "assets"))
        FileUtils.copyAssetsDir2Phone(app, "Packages", "assets");
}

4.3 創建虛擬形象

前面在https://doc-zh.zego.im/article/15302下載SDK包含了helper目錄,這個目錄里面有非常重要的兩個檔案:

在這里插入圖片描述

其中ZegoCharacterHelper檔案是個介面定義類,即雖然是個類,但具體的實作全部在ZegoCharacterHelperImpl中,我們先一睹為快,看看ZegoCharacterHelper包含了哪些可處理的屬性:

public class ZegoCharacterHelper {
    public static final String MODEL_ID_MALE = "male";
    public static final String MODEL_ID_FEMALE = "female";
    //****************************** 捏臉維度的 key 值 ******************************/
    public static final String FACESHAPE_BROW_SIZE_Y = "faceshape_brow_size_y";// 眉毛厚度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_BROW_SIZE_X = "faceshape_brow_size_x";// 眉毛長度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_BROW_ALL_Y = "faceshape_brow_all_y";// 眉毛高度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_BROW_ALL_ROLL_Z = "faceshape_brow_all_roll_z";// 眉毛旋轉, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_EYE_SIZE = "faceshape_eye_size"; // 眼睛大小, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_EYE_SIZE_Y = "faceshape_eye_size_y";
    public static final String FACESHAPE_EYE_ROLL_Y = "faceshape_eye_roll_y";// 眼睛高度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_EYE_ROLL_Z = "faceshape_eye_roll_z";// 眼睛旋轉, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_EYE_X = "faceshape_eye_x";// 雙眼眼距, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_NOSE_ALL_X = "faceshape_nose_all_x";// 鼻子寬度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_NOSE_ALL_Y = "faceshape_nose_all_y";// 鼻子高度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_NOSE_SIZE_Z = "faceshape_nose_size_z";
    public static final String FACESHAPE_NOSE_ALL_ROLL_Y = "faceshape_nose_all_roll_y";// 鼻頭旋轉, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_NOSTRIL_ROLL_Y = "faceshape_nostril_roll_y";// 鼻翼旋轉, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_NOSTRIL_X = "faceshape_nostril_x";
    public static final String FACESHAPE_MOUTH_ALL_Y = "faceshape_mouth_all_y";// 嘴巴上下, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_LIP_ALL_SIZE_Y = "faceshape_lip_all_size_y";// 嘴唇厚度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_LIPCORNER_Y = "faceshape_lipcorner_y";// 嘴角旋轉, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_LIP_UPPER_SIZE_X = "faceshape_lip_upper_size_x"; // 上唇寬度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_LIP_LOWER_SIZE_X = "faceshape_lip_lower_size_x"; // 下唇寬度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_JAW_ALL_SIZE_X = "faceshape_jaw_all_size_x";// 下巴寬度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_JAW_Y = "faceshape_jaw_y";// 下巴高度, 取值范圍0.0-1.0,默認值0.5
    public static final String FACESHAPE_CHEEK_ALL_SIZE_X = "faceshape_cheek_all_size_x";// 臉頰寬度, 取值范圍0.0-1.0,默認值0.5

    //其他函式略
}

可以看到,上面資料基本包含所有人臉屬性了,基本具備了捏臉能力,篇幅原因,我們這里不具體去實作,有這方面需求的讀者,可以通過在界面上調整上面相關屬性來實作,

接下來我們開始創建虛擬形象,首先創建一個User物體類:


public class User {
    public String userName; //用戶名
    public String userId; //用戶ID
    public boolean isMan; //性別
    public int width; //預覽寬度
    public int height; //預覽高度
    public int bgColor; //背景顏色
    public int shirtIdx = 0; // T-shirt資源id
    public int browIdx = 0; //眉毛資源id

    public User(String userName, String userId, int width, int height) {
        this.userName = userName;
        this.userId = userId;
        this.width = width;
        this.height = height;
        this.isMan = true;
        bgColor = Color.argb(255, 33, 66, 99);
    }  
}

示例作用,為了簡單起見,我們這里只針對眉毛和衣服資源做選取,接下來創建一個Activity:

public class AvatarActivity extends BaseActivity  {
    private int vWidth = 720;
    private int vHeight = 1080; 
    private User user = new User("C_0001", "C_0001", vWidth, vHeight);
    private TextureView mTextureView;  //用于顯示Avatar形象
    private AvatarMngr mAvatarMngr; // 用于維護管理Avatar
    private ColorPickerDialog colorPickerDialog; //用于背景色選取

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_avatar);
        
        // ....
        // 其他初始化界面相關代碼略...
        // ....

        user.isMan = true;
        mTextureView = findViewById(R.id.avatar_view);  
        mZegoMngr = ZegoMngr.getInstance(getApplication()); 
        // 開啟虛擬形象預覽
        mAvatarMngr.start(mTextureView, user);
    }

最后一行代碼中開啟了虛擬形象預覽,那么這里開啟虛擬形象預覽具體做了哪些作業呢?show me the code:

public class AvatarMngr implements ZegoAvatarServiceDelegate, RTCMngr.CaptureListener {
    private static final String TAG = "AvatarMngr";
    private static AvatarMngr mInstance;
    private boolean mIsStop = false;

    private User mUser = null;
    private TextureBgRender mBgRender = null;
    private OnAvatarServiceInitSucced listener;
    private ZegoCharacterHelper mCharacterHelper;
    private Application mApp;

    public interface OnAvatarServiceInitSucced {
        void onInitSucced();
    }

    public void setLicense(String license, OnAvatarServiceInitSucced listener) {
        this.listener = listener;
        ZegoAvatarService.addServiceObserver(this);
        String aiPath = FileUtils.getPhonePath(mApp, "AIModel.bundle", "assets"); //   AI 模型的絕對路徑
        ZegoServiceConfig config = new ZegoServiceConfig(license, aiPath);
        ZegoAvatarService.init(mApp, config);
    }

    public void stop() {
        mIsStop = true;
        mUser = null;
        stopExpression();
    }

    public void updateUser(User user) {
        mUser = user;
        if (user.shirtIdx == 0) {
            mCharacterHelper.setPackage("m-shirt01");
        } else {
            mCharacterHelper.setPackage("m-shirt02");
        }
        if (user.browIdx == 0) {
            mCharacterHelper.setPackage("brows_1");
        } else {
            mCharacterHelper.setPackage("brows_2");
        }
    }

    /**
     * 啟動Avatar,呼叫此函式之前,請確保已經呼叫過setLicense
     *
     * @param avatarView
     */
    public void start(TextureView avatarView, User user) {
        mUser = user;
        mIsStop = false;
        initAvatar(avatarView, user);
        startExpression();
    }

    private void initAvatar(TextureView avatarView, User user) {
        String sex = ZegoCharacterHelper.MODEL_ID_MALE;
        if (!user.isMan) sex = ZegoCharacterHelper.MODEL_ID_FEMALE;

        // 創建 helper 簡化呼叫
        // base.bundle 是頭模, human.bundle 是全身人模
        mCharacterHelper = new ZegoCharacterHelper(FileUtils.getPhonePath(mApp, "human.bundle", "assets"));
        mCharacterHelper.setExtendPackagePath(FileUtils.getPhonePath(mApp, "Packages", "assets"));
        // 設定形象配置
        mCharacterHelper.setDefaultAvatar(sex);
        updateUser(user);
        // 獲取當前妝容資料, 可以保存到用戶資料中
        String json = mCharacterHelper.getAvatarJson();

    }

    // 啟動表情檢測
    private void startExpression() {
        // 啟動表情檢測前要申請攝像頭權限, 這里是在 MainActivity 已經申請過了
        ZegoAvatarService.getInteractEngine().startDetectExpression(ZegoExpressionDetectMode.Camera, expression -> {
            // 表情直接塞給 avatar 驅動
            mCharacterHelper.setExpression(expression);
        });
    }

    // 停止表情檢測
    private void stopExpression() {
        // 不用的時候記得停止
        ZegoAvatarService.getInteractEngine().stopDetectExpression();
    }

    // 獲取到 avatar 紋理后的處理
    public void onCaptureAvatar(int textureId, int width, int height) {
        if (mIsStop || mUser == null) { // rtc 的 onStop 是異步的, 可能activity已經運行到onStop了, rtc還沒
            return;
        }
        boolean useFBO = true;
        if (mBgRender == null) {
            mBgRender = new TextureBgRender(textureId, useFBO, width, height, Texture2dProgram.ProgramType.TEXTURE_2D_BG);
        }
        mBgRender.setInputTexture(textureId);
        float r = Color.red(mUser.bgColor) / 255f;
        float g = Color.green(mUser.bgColor) / 255f;
        float b = Color.blue(mUser.bgColor) / 255f;
        float a = Color.alpha(mUser.bgColor) / 255f;
        mBgRender.setBgColor(r, g, b, a);
        mBgRender.draw(useFBO); // 畫到 fbo 上需要反向的
        ZegoExpressEngine.getEngine().sendCustomVideoCaptureTextureData(mBgRender.getOutputTextureID(), width, height, System.currentTimeMillis());


    }

    @Override
    public void onStartCapture() {
        if (mUser == null) return;
//        // 收到回呼后,開發者需要執行啟動視頻采集相關的業務邏輯,例如開啟攝像頭等
        AvatarCaptureConfig config = new AvatarCaptureConfig(mUser.width, mUser.height);
//        // 開始捕獲紋理
        mCharacterHelper.startCaptureAvatar(config, this::onCaptureAvatar);
    }

    @Override
    public void onStopCapture() {
        mCharacterHelper.stopCaptureAvatar();
        stopExpression();
    }


    private void initRes(Application app) {
        // 先把資源拷貝到SD卡,注意:線上使用時,需要做一下判斷,避免多次拷貝,資源也可以做成從網路下載,
        if (!FileUtils.checkFile(app, "AIModel.bundle", "assets"))
            FileUtils.copyAssetsDir2Phone(app, "AIModel.bundle", "assets");
        if (!FileUtils.checkFile(app, "base.bundle", "assets"))
            FileUtils.copyAssetsDir2Phone(app, "base.bundle", "assets");
        if (!FileUtils.checkFile(app, "human.bundle", "assets"))
            FileUtils.copyAssetsDir2Phone(app, "human.bundle", "assets");
        if (!FileUtils.checkFile(app, "Packages", "assets"))
            FileUtils.copyAssetsDir2Phone(app, "Packages", "assets");

    }

    @Override
    public void one rror(ZegoAvatarErrorCode code, String desc) {
        Log.e(TAG, "errorcode : " + code.getErrorCode() + ",desc : " + desc);
    }

    @Override
    public void onStateChange(ZegoAvatarServiceState state) {
        if (state == ZegoAvatarServiceState.InitSucceed) {
            Log.i("ZegoAvatar", "Init success");
            // 要記得及時移除通知
            ZegoAvatarService.removeServiceObserver(this);
            if (listener != null) listener.onInitSucced();
        }
    }

    private AvatarMngr(Application app) {
        mApp = app;
        initRes(app);
    }

    public static AvatarMngr getInstance(Application app) {
        if (null == mInstance) {
            synchronized (AvatarMngr.class) {
                if (null == mInstance) {
                    mInstance = new AvatarMngr(app);
                }
            }
        }
        return mInstance;
    }
}

以上代碼完成了整個虛擬形象的創建,關鍵代碼全部展示,如果還需要具體全部代碼,直接從附件中下載即可,

5 附件

  • 原始碼:https://github.com/RTCWang/Meta-Avatar
  • 即構元宇宙官網:https://doc-zh.zego.im/article/15302

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/539754.html

標籤:其他

上一篇:TheFuck—Python寫的超實用命令糾正工具

下一篇:軟工綜合實踐課設——員工招聘系統(參考BOSS直聘);Pyhton實作

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more