跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C#大前端算法

Unity 2022 + UXR 3.0:Rokid AR 眼镜轻量级消消乐开发实录

基于 Unity 2022 LTS 与 Rokid UXR 3.0 SDK,实现 AR 眼镜端轻量级消消乐游戏。通过解耦场景初始化、状态管理与棋盘逻辑,解决空间锚定、射线交互及递归消除等核心问题。分享代码实现细节、协程动画处理及字体缺失、Z 轴深度等常见调试坑点,提供 AR 应用开发的工程化思路。

片刻发布于 2026/4/9更新于 2026/6/1324 浏览

体验开场

戴上 Rokid AR Lite,原本平淡的办公桌面瞬间变得有趣。设备启动后,一块晶莹剔透的 8×8 宝石棋盘凭空浮现,稳稳锚定在真实空间里。转动头部时,能清晰感受到棋盘的厚度感。

界面左上角实时跳动 Score,右上角显示剩余 Moves。伸出手利用射线交互滑动两颗宝石,清脆音效伴随粒子消散,三颗同色宝石瞬间消除,上方宝石顺势滑落填补空缺。这不是科幻电影,而是基于 Unity 2022 LTS 与 Rokid UXR 3.0 SDK 开发的轻量级 AR 尝试。将经典玩法带入空间计算时代,背后的'空间映射'思路和开发细节值得每一位 AR 开发者思考。

棋盘效果


技术栈简介

决定开发这个 AR 小游戏时,我们选定了这套'黄金组合':

  • Unity 2022.3 LTS:长期支持版本,性能优化和第三方库兼容性极佳。AR 开发对底层稳定性要求高,LTS 版能有效避免编辑器 Bug。
  • Rokid UXR 3.0 SDK:官方核心套件,集成 AR 相机、6DoF/3DoF 定位、手势交互及射线检测。3.0 版本在资源占用和 API 易用性上做了大量减法,适合快速原型验证。

核心原因只有两个字:效率。消消乐逻辑成熟,我们不希望把精力浪费在配置 Android 环境或调试传感器数据上。Unity 2022 的成熟生态加上 UXR 3.0 的'开箱即用',能让我们在短时间内完成从构思到真机运行。这种快速闭环能力,对产品团队验证新交互形态至关重要。


思路迁移

传统手机屏幕是纯粹的 2D 体验,AR 场景下则需进行'升维重构'。

结构差异
  • 棋盘(Board):不再是背景图,而是悬浮在空间中的'逻辑面板'。必须有明确的 3D 坐标(Z 轴),确保用户无视觉压迫感。
  • 宝石(Gem):拥有 Collider 的游戏对象,响应空间射线触发,而非单纯屏幕点击。
  • UI 系统:避免放在屏幕四角导致眼睛频繁调焦。我们将 UI 固定在棋盘上方左右两侧,随棋盘一起锚定在空间中。

UI 布局示意

交互映射

手机操作是手指拖拽,Rokid AR 眼镜支持手持终端射线或手势追踪。我们将玩家的射线输入映射到 3D 空间的物理射线检测(Raycast)上。当射线击中宝石并产生滑动位移时,逻辑层计算向量方向(上下左右),触发交换。


核心实现思路

实现层面采用高度解耦设计,将场景初始化、游戏管理、棋盘逻辑、单体交互和预制体生成完全分开。好处是即便未来要把宝石从'圆球'换成'3D 恐龙',逻辑代码也几乎无需修改。

场景搭建与一键初始化 (GameSetup)

AR 项目中,手动在 Hierarchy 面板拖入几十个宝石太繁琐。我们编写了 GameSetup.cs,作为游戏的'发令枪',运行瞬间自动创建相机、Canvas、文本,并根据代码生成的预制体初始化棋盘。

关键代码:GameSetup.cs

using UnityEngine;
using UnityEngine.UI;

public class GameSetup : MonoBehaviour {
    void Start() {
        SetupGame();
    }

    void SetupGame() {
        // 创建主摄像机
        Camera mainCamera = Camera.main;
        if (mainCamera != null) {
            mainCamera.transform.position = new Vector3(0, 0, -10);
            mainCamera.orthographic = true;
            mainCamera.orthographicSize = 5;
            mainCamera.backgroundColor = new Color(0.1f, 0.1f, 0.15f); // 暗色背景有利于 AR 剔除
        }

        // 创建 Canvas
        GameObject canvasObj = new GameObject("Canvas");
        Canvas canvas = canvasObj.AddComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        canvasObj.AddComponent<CanvasScaler>();
        canvasObj.AddComponent<GraphicRaycaster>();

        // 创建分数文本
        GameObject scoreObj = new GameObject("ScoreText");
        scoreObj.transform.SetParent(canvasObj.transform);
        Text scoreText = scoreObj.AddComponent<Text>();
        scoreText.text = "Score: 0";
        scoreText.fontSize = 36;
        scoreText.color = Color.white;
        scoreText.alignment = TextAnchor.UpperLeft;
        scoreText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
        RectTransform scoreRect = scoreObj.GetComponent<RectTransform>();
        scoreRect.anchorMin = new Vector2(0, 1);
        scoreRect.anchorMax = new Vector2(0, 1);
        scoreRect.pivot = new Vector2(0, 1);
        scoreRect.anchoredPosition = new Vector2(20, -20);
        scoreRect.sizeDelta = new Vector2(300, 50);

        // 创建移动次数文本
        GameObject movesObj = new GameObject("MovesText");
        movesObj.transform.SetParent(canvasObj.transform);
        Text movesText = movesObj.AddComponent<Text>();
        movesText.text = "Moves: 30";
        movesText.fontSize = 36;
        movesText.color = Color.white;
        movesText.alignment = TextAnchor.UpperRight;
        movesText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
        RectTransform movesRect = movesObj.GetComponent<RectTransform>();
        movesRect.anchorMin = new Vector2(1, 1);
        movesRect.anchorMax = new Vector2(1, 1);
        movesRect.pivot = new Vector2(1, 1);
        movesRect.anchoredPosition = new Vector2(-20, -20);
        movesRect.sizeDelta = new Vector2(300, 50);

        // 创建 GameManager
        GameObject gmObj = new GameObject("GameManager");
        GameManager gm = gmObj.AddComponent<GameManager>();
        gm.scoreText = scoreText;
        gm.movesText = movesText;

        // 创建宝石预制体
        GameObject[] gemPrefabs = new GameObject[6];
        Color[] colors = new Color[] {
            new Color(1f, 0.2f, 0.2f), // 红色
            new Color(0.2f, 0.8f, 0.2f), // 绿色
            new Color(0.3f, 0.5f, 1f), // 蓝色
            new Color(1f, 0.9f, 0.2f), // 黄色
            new Color(0.9f, 0.3f, 1f), // 紫色
            new Color(1f, 0.6f, 0.2f) // 橙色
        };
        string[] names = new string[] { "RedGem", "GreenGem", "BlueGem", "YellowGem", "PurpleGem", "OrangeGem" };
        for (int i = 0; i < 6; i++) {
            gemPrefabs[i] = GemPrefabCreator.CreateGemPrefab(colors[i], names[i]);
            gemPrefabs[i].SetActive(false);
        }

        // 创建棋盘
        GameObject boardObj = new GameObject("Board");
        Board board = boardObj.AddComponent<Board>();
        board.width = 8;
        board.height = 8;
        board.gemSpacing = 1f;
        board.gemPrefabs = gemPrefabs;
        Debug.Log("消消乐游戏设置完成!");
    }
}

这段脚本的核心意义在于将复杂的编辑器操作变成'一键式'。在 AR 开发中,我们经常需要调整棋盘在空间中的相对位置。通过修改 mainCamera.transform.position 或者棋盘的 Z 轴偏移,可以瞬间改变游戏的深度感。

初始化流程

游戏状态管理 (GameManager)

GameManager 是游戏的'大脑',不关心宝石怎么消掉,只关心:多少分了?还剩几步?游戏结束了吗?

关键代码:GameManager.cs

using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour {
    public Text scoreText;
    public Text movesText;
    private int score = 0;
    private int moves = 30;

    void Start() {
        UpdateUI();
    }

    public void AddScore(int points) {
        score += points;
        UpdateUI();
    }

    public void UseMove() {
        moves--;
        UpdateUI();
        if (moves <= 0) {
            GameOver();
        }
    }

    void UpdateUI() {
        if (scoreText != null) scoreText.text = "Score: " + score;
        if (movesText != null) movesText.text = "Moves: " + moves;
    }

    void GameOver() {
        Debug.Log("Game Over! Final Score: " + score);
    }

    public void RestartGame() {
        UnityEngine.SceneManagement.SceneManager.LoadScene(
            UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
    }
}

这里值得注意的一点是,我们在 UI 更新时进行了非空判断。在 AR 项目里,UI 加载顺序有时会因为 SDK 初始化而略有延迟,这种健壮性处理是必须的。

核心递归逻辑:棋盘生成与消除 (Board)

这是整个游戏最核心、也最复杂的部分。我们需要处理 8×8 的二维数组,并且要保证:

  1. 初始化无解:开局不能直接有三连。
  2. 交换检测:只有交换后能产生消除的操作才是合法的。
  3. 连锁反应:一次消除后,宝石掉落,可能产生新的消除,这需要用到递归(Recursive)或者协程(Coroutine)。

关键代码:Board.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Board : MonoBehaviour {
    public int width = 8;
    public int height = 8;
    public float gemSpacing = 1f;
    public GameObject[] gemPrefabs;

    private Gem[,] gems;
    private bool isProcessing = false;
    private int movingGems = 0;
    private GameManager gameManager;

    void Start() {
        gameManager = FindObjectOfType<GameManager>();
        gems = new Gem[width, height];
        SetupBoard();
    }

    void SetupBoard() {
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                int randomGemType = Random.Range(0, gemPrefabs.Length);
                // 避免初始就有匹配
                while (HasMatchOnFill(x, y, randomGemType)) {
                    randomGemType = Random.Range(0, gemPrefabs.Length);
                }
                CreateGem(x, y, randomGemType);
            }
        }
        // 居中棋盘,方便在 AR 眼镜中观察
        transform.position = new Vector3(-width * gemSpacing / 2f + gemSpacing / 2f,
                                         -height * gemSpacing / 2f + gemSpacing / 2f, 0);
    }

    bool HasMatchOnFill(int column, int row, int gemType) {
        if (column > 1 && gems[column - 1, row]?.gemType == gemType && gems[column - 2, row]?.gemType == gemType)
            return true;
        if (row > 1 && gems[column, row - 1]?.gemType == gemType && gems[column, row - 2]?.gemType == gemType)
            return true;
        return false;
    }

    void CreateGem(int x, int y, int gemType) {
        Vector2 position = new Vector2(x * gemSpacing, y * gemSpacing);
        GameObject gem = Instantiate(gemPrefabs[gemType], position, Quaternion.identity);
        gem.transform.parent = transform;
        gem.SetActive(true);
        Gem gemScript = gem.GetComponent<Gem>();
        gemScript.SetPosition(x, y);
        gemScript.gemType = gemType;
        gems[x, y] = gemScript;
    }

    public void SwapGems(int col1, int row1, int col2, int row2) {
        if (isProcessing) return;
        if (col2 < 0 || col2 >= width || row2 < 0 || row2 >= height) return;
        Gem gem1 = gems[col1, row1];
        Gem gem2 = gems[col2, row2];
        if (gem1 != null && gem2 != null)
            StartCoroutine(SwapGemsCoroutine(gem1, gem2));
    }

    IEnumerator SwapGemsCoroutine(Gem gem1, Gem gem2) {
        isProcessing = true;
        int tempCol = gem1.column, tempRow = gem1.row;
        gem1.SetPosition(gem2.column, gem2.row);
        gem2.SetPosition(tempCol, tempRow);
        gems[gem1.column, gem1.row] = gem1;
        gems[gem2.column, gem2.row] = gem2;
        gem1.MoveTo(new Vector3(gem1.column * gemSpacing, gem1.row * gemSpacing, 0));
        gem2.MoveTo(new Vector3(gem2.column * gemSpacing, gem2.row * gemSpacing, 0));
        yield return new WaitForSeconds(0.3f);

        List<Gem> matches = FindAllMatches();
        if (matches.Count == 0) {
            // 回退逻辑
            gem1.SetPosition(gem2.column, gem2.row);
            gem2.SetPosition(tempCol, tempRow);
            gems[gem1.column, gem1.row] = gem1;
            gems[gem2.column, gem2.row] = gem2;
            gem1.MoveTo(new Vector3(gem1.column * gemSpacing, gem1.row * gemSpacing, 0));
            gem2.MoveTo(new Vector3(gem2.column * gemSpacing, gem2.row * gemSpacing, 0));
            yield return new WaitForSeconds(0.3f);
        } else {
            gameManager.UseMove();
            yield return StartCoroutine(ProcessMatches(matches));
        }
        isProcessing = false;
    }

    List<Gem> FindAllMatches() {
        List<Gem> matches = new List<Gem>();
        // 横向检测
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width - 2; x++) {
                if (gems[x, y] != null && gems[x + 1, y] != null && gems[x + 2, y] != null) {
                    if (gems[x, y].gemType == gems[x + 1, y].gemType && gems[x, y].gemType == gems[x + 2, y].gemType) {
                        if (!matches.Contains(gems[x, y])) matches.Add(gems[x, y]);
                        if (!matches.Contains(gems[x + 1, y])) matches.Add(gems[x + 1, y]);
                        if (!matches.Contains(gems[x + 2, y])) matches.Add(gems[x + 2, y]);
                    }
                }
            }
        }
        // 纵向检测类似逻辑...
        return matches;
    }

    IEnumerator ProcessMatches(List<Gem> matches) {
        gameManager.AddScore(matches.Count * 10);
        foreach (Gem gem in matches) {
            gems[gem.column, gem.row] = null;
            Destroy(gem.gameObject);
        }
        yield return new WaitForSeconds(0.3f);
        yield return StartCoroutine(DropGems());
        yield return StartCoroutine(FillBoard());

        List<Gem> newMatches = FindAllMatches();
        if (newMatches.Count > 0)
            yield return StartCoroutine(ProcessMatches(newMatches));
    }

    IEnumerator DropGems() {
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if (gems[x, y] == null) {
                    for (int yAbove = y + 1; yAbove < height; yAbove++) {
                        if (gems[x, yAbove] != null) {
                            gems[x, y] = gems[x, yAbove];
                            gems[x, yAbove] = null;
                            gems[x, y].SetPosition(x, y);
                            gems[x, y].MoveTo(new Vector3(x * gemSpacing, y * gemSpacing, 0));
                            break;
                        }
                    }
                }
            }
        }
        yield return new WaitForSeconds(0.5f);
    }

    IEnumerator FillBoard() {
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if (gems[x, y] == null) {
                    int randomGemType = Random.Range(0, gemPrefabs.Length);
                    Vector2 spawnPosition = new Vector2(x * gemSpacing, height * gemSpacing);
                    GameObject gem = Instantiate(gemPrefabs[randomGemType], spawnPosition, Quaternion.identity);
                    gem.transform.parent = transform;
                    Gem gemScript = gem.GetComponent<Gem>();
                    gemScript.SetPosition(x, y);
                    gemScript.gemType = randomGemType;
                    gemScript.MoveTo(new Vector3(x * gemSpacing, y * gemSpacing, 0));
                    gems[x, y] = gemScript;
                }
            }
        }
        yield return new WaitForSeconds(0.5f);
    }

    public void OnGemMoveComplete() {
        movingGems--;
    }
}

在 AR 开发中,IEnumerator(协程)是极其好用的工具。因为宝石的下落和移动必须是平滑的动画,如果直接改变位置,玩家会觉得画面闪烁。通过协程配合 WaitForSeconds,能给大脑一个处理空间变化的缓冲区。

宝石的微观交互 (Gem)

每一颗宝石都是独立个体,需要知道自己被'摸'到了,并且知道被拖向了哪个方向。

关键代码:Gem.cs

using UnityEngine;

public class Gem : MonoBehaviour {
    public int column;
    public int row;
    public int gemType;
    private Board board;
    private Vector2 firstTouchPosition;
    private Vector2 finalTouchPosition;
    private bool isMoving = false;
    private Vector3 targetPosition;
    public float moveSpeed = 10f;

    void Start() {
        board = FindObjectOfType<Board>();
    }

    void Update() {
        if (isMoving) {
            transform.position = Vector3.Lerp(transform.position, targetPosition, moveSpeed * Time.deltaTime);
            if (Vector3.Distance(transform.position, targetPosition) < 0.01f) {
                transform.position = targetPosition;
                isMoving = false;
                board.OnGemMoveComplete();
            }
        }

        // 这里的 MouseButton 输入在 Rokid UXR 下会被自动映射为射线点击
        if (Input.GetMouseButtonDown(0)) {
            Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            RaycastHit2D hit = Physics2D.Raycast(mousePos, Vector2.zero);
            if (hit.collider != null && hit.collider.gameObject == gameObject)
                firstTouchPosition = mousePos;
        }

        if (Input.GetMouseButtonUp(0)) {
            Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            RaycastHit2D hit = Physics2D.Raycast(firstTouchPosition, Vector2.zero);
            if (hit.collider != null && hit.collider.gameObject == gameObject) {
                finalTouchPosition = mousePos;
                CalculateAngle();
            }
        }
    }

    void CalculateAngle() {
        Vector2 direction = finalTouchPosition - firstTouchPosition;
        if (direction.magnitude < 0.5f) return;
        if (Mathf.Abs(direction.x) > Mathf.Abs(direction.y)) {
            if (direction.x > 0) board.SwapGems(column, row, column + 1, row);
            else board.SwapGems(column, row, column - 1, row);
        } else {
            if (direction.y > 0) board.SwapGems(column, row, column, row + 1);
            else board.SwapGems(column, row, column, row - 1);
        }
    }

    public void MoveTo(Vector3 newPosition) {
        targetPosition = newPosition;
        isMoving = true;
    }

    public void SetPosition(int col, int r) {
        column = col;
        row = r;
    }
}

这里有一个'产品级'的细节:我们在计算方向时,加入了 direction.magnitude < 0.5f 的判断。这是为了防止玩家轻微抖动导致的误触。在 AR 环境下,由于你是对着空气操作,射线的稳定性不如物理屏幕,这个缓冲区(Deadzone)非常重要。

交互反馈

运行时预制体生成 (GemPrefabCreator)

为了让项目包体尽可能小,我们没有使用精美的 3D 模型,而是通过代码在运行时绘制了一张 128x128 的带渐变的圆形贴图。这种'纯程序化'的资源生成方式,极其适合快速原型。

关键代码:GemPrefabCreator.cs

using UnityEngine;

public class GemPrefabCreator : MonoBehaviour {
    public static GameObject CreateGemPrefab(Color color, string name) {
        GameObject gem = new GameObject(name);
        SpriteRenderer sr = gem.AddComponent<SpriteRenderer>();
        Texture2D texture = new Texture2D(128, 128);
        Color[] pixels = new Color[128 * 128];
        Vector2 center = new Vector2(64, 64);
        float radius = 60;

        for (int y = 0; y < 128; y++) {
            for (int x = 0; x < 128; x++) {
                float distance = Vector2.Distance(new Vector2(x, y), center);
                if (distance < radius)
                    pixels[y * 128 + x] = color * (1f - (distance / radius) * 0.3f);
                else
                    pixels[y * 128 + x] = Color.clear;
            }
        }
        texture.SetPixels(pixels);
        texture.Apply();
        sr.sprite = Sprite.Create(texture, new Rect(0, 0, 128, 128), new Vector2(0.5f, 0.5f), 128);
        gem.AddComponent<CircleCollider2D>().radius = 0.45f;
        gem.AddComponent<Gem>();
        return gem;
    }
}

程序化纹理


开发过程中的小坑与经验

说实话,即便代码逻辑写对了,第一次运行项目时,大概率还是会遇到'翻车'现场。这里总结了几个我们在调试时遇到的典型坑。

消失的字体:Arial.ttf 去哪了?

这是 Unity 新版本的'著名槽点'。在 Unity 2022 LTS 中,如果你像以前一样直接引用内置的 Arial.ttf,编译成 APK 后,眼镜里的文字全部变成了空白。控制台会疯狂报:ArgumentException: Arial.ttf is no longer a valid built in font。

避坑指南: 现在的内置字体名字改成了 LegacyRuntime.ttf。在代码中加载时一定要写对,或者最稳妥的方法是:自己导入一个开源的中文字体库。

字体报错

只有 UI 没棋盘?三步排查法

很多时候运行起来,能看到 Score 和 Moves 在跳,但眼前一片漆黑,宝石棋盘没了。这时候不要慌,按以下顺序排查:

  1. 棋盘坐标 (Z-depth):AR 相机的剪裁平面(Clipping Planes)通常设置得比较窄。如果你的棋盘 Z 轴坐标设置成了 0,而相机在 -10 且正交尺寸不对,棋盘可能就在你的视线之外。
  2. Layer 问题:检查生成的 Board 节点是否在默认层。如果误分到了不可见的 Layer,它就像隐身了一样。
  3. Inspector 赋值检测:如果是通过编辑器拖入的预制体,检查 gemPrefabs 数组有没有漏填。哪怕空了一项,代码在实例化时就会中断,导致整个棋盘崩溃。
构建设置与分辨率

针对 Rokid AR Lite,务必确保:

  • 色彩空间:尽量选择 Linear。
  • 渲染管线:建议开启 URP,以获得更好的抗锯齿效果,否则宝石边缘会有严重的锯齿感。
  • 多线程渲染:开启它可以显著降低眼镜的功耗和发热。

实机体验与价值

当这个'轻量级'消消乐真正跑在 Rokid AR Lite 上时,其带来的产品启示远超游戏本身。

实机体感

实测时,我最喜欢的一种体验是:将棋盘挂在显示器的正上方。当我写代码写累了,不需要低头找手机(这个动作对颈椎极不友好),只需要轻轻一抬头,对着空中的宝石滑几下。

实机演示


总结与展望

利用 Unity 2022 LTS + UXR 3.0 SDK,我们完成了一个从零到一的 AR 探索。这套组合的优点显而易见:生态成熟、开发门槛低、真机适配顺滑。

当然,目前这仅仅是一个'毛坯房'。接下来,我们可以迭代的方向还有很多:

  1. 音效与反馈:加入立体空间音频,让宝石爆破的声音从棋盘所在的方位传来。
  2. 视觉特效:引入 URP 的粒子系统,让消除瞬间火花四溅。
  3. 深度交互:接入手势追踪。想象一下,用食指和中指'捏'住宝石进行物理拖拽,那才是真正的空间计算体验。
  4. 关卡设计:引入不同的障碍物(冰块、木板),将这套轻量级原型打造成完整的商业产品。

AR 开发不需要一上来就追求宏大的叙事,有时候,在现实世界里安安稳稳地玩上一局消消乐,就是空间计算带给我们的最纯粹的快乐。

目录

  1. 体验开场
  2. 技术栈简介
  3. 思路迁移
  4. 结构差异
  5. 交互映射
  6. 核心实现思路
  7. 场景搭建与一键初始化 (GameSetup)
  8. 游戏状态管理 (GameManager)
  9. 核心递归逻辑:棋盘生成与消除 (Board)
  10. 宝石的微观交互 (Gem)
  11. 运行时预制体生成 (GemPrefabCreator)
  12. 开发过程中的小坑与经验
  13. 消失的字体:Arial.ttf 去哪了?
  14. 只有 UI 没棋盘?三步排查法
  15. 构建设置与分辨率
  16. 实机体验与价值
  17. 实机体感
  18. 总结与展望
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • GLM-4.6V-Flash 多模态模型:Jupyter 与 Web 双入口部署指南
  • Stable Diffusion 扩散模型原理与 PyTorch 实现
  • 10 款 AI 降重工具实测对比与选择指南
  • NestJS 接口响应 message 编写规范与 API 提示信息标准化
  • Spring IoC 与依赖注入详解
  • 国产化服务器部署:银河麒麟系统搭建 Web 服务实战
  • go2rtc 开源视频流媒体协议转换工具
  • Python 入门指南:基础语法与开发环境配置
  • Windows 上安装 Python-vLLM 的两种方法
  • SpringBoot 整合 Neo4j 图数据库实战指南
  • OpenClaw 接入飞书机器人并集成 Ollama 本地大模型实战
  • 中国人工智能大模型技术白皮书核心内容解读
  • CSS 常用标签与属性详解
  • 彻底关闭Win10中烦人的365 Copilot弹窗的6种方法
  • 具身智能里程碑:π0 视觉 - 语言 - 动作流模型解析
  • Android 应用安全加固与防破解措施分析
  • Python __main__.py 文件详解
  • Nano Banana 生成中文模糊?结合 Seedream 4.5 实现高清重绘
  • Git 安装配置及基础使用步骤
  • 谷歌与OpenAI合作利用搜索增强大模型知识时效性

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online