《用MATLAB玩转游戏开发》推箱子游戏的MATLAB趣味实现

《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》第二阶段:进阶篇(动画与算法)-推箱子游戏的MATLAB趣味实现 🎮

文章目录

  • [《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》第二阶段:进阶篇(动画与算法)-推箱子游戏的MATLAB趣味实现 🎮](#《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》第二阶段:进阶篇(动画与算法)-推箱子游戏的MATLAB趣味实现 🎮)
    • [一、游戏介绍与设计思路 🧠](#一、游戏介绍与设计思路 🧠)
    • [二、实现原理与技术细节 ⚙️](#二、实现原理与技术细节 ⚙️)
      • [1. 游戏数据结构](#1. 游戏数据结构)
      • [2. 图形渲染](#2. 图形渲染)
      • [3. 游戏逻辑](#3. 游戏逻辑)
    • [三、完整脑图与流程图 🗺️](#三、完整脑图与流程图 🗺️)
    • [四、分步实现教程 🛠️](#四、分步实现教程 🛠️)
      • [1. 初始化游戏环境](#1. 初始化游戏环境)
      • [2. 关卡设计](#2. 关卡设计)
      • [3. 游戏主循环](#3. 游戏主循环)
      • [4. 渲染游戏地图](#4. 渲染游戏地图)
      • [5. 键盘控制处理](#5. 键盘控制处理)
      • [6. 胜利条件检查](#6. 胜利条件检查)
      • [7. 保存游戏GIF](#7. 保存游戏GIF)
    • [五、完整可运行代码 🏗️](#五、完整可运行代码 🏗️)
    • [六、游戏运行与扩展建议 🚀](#六、游戏运行与扩展建议 🚀)
    • [七、总结与收获 📚](#七、总结与收获 📚)

大家好!今天我要带大家用MATLAB实现一个经典游戏------推箱子!通过这个项目,你不仅能学习MATLAB的图形界面编程,还能掌握游戏开发的基本原理。让我们开始这段有趣的编程之旅吧!🚀

一、游戏介绍与设计思路 🧠

推箱子(Sokoban)是一款经典的益智游戏,玩家需要将箱子推到指定位置。我们的MATLAB版本将包含以下特点:

  • 🎨 图形化界面显示
  • 🎵 背景音乐播放
  • 📸 游戏截图保存为GIF
  • 🕹️ 键盘控制角色移动
  • 🏆 关卡设计功能

游戏元素设计

  1. 角色:玩家控制的推箱子小人
  2. 墙壁:不可穿越的障碍物
  3. 箱子:可以被推动的物体
  4. 目标点:箱子需要被推到的位置
  5. 地面:可通行的区域

二、实现原理与技术细节 ⚙️

1. 游戏数据结构

我们将使用矩阵来表示游戏地图:

  • 0: 空地
  • 1: 墙壁
  • 2: 箱子
  • 3: 目标点
  • 4: 角色
  • 5: 箱子在目标点上
  • 6: 角色在目标点上

2. 图形渲染

使用MATLAB的imageimshow函数来显示游戏地图,通过颜色映射来区分不同元素。

3. 游戏逻辑

  • 移动检测:检查目标位置是否可移动
  • 推动检测:检查箱子能否被推动
  • 胜利条件:所有箱子都位于目标点上

三、完整脑图与流程图 🗺️

脑图 (游戏架构)

流程图

方向键 是 否 退出键 开始 初始化游戏 显示游戏界面 等待玩家输入 处理移动 更新游戏状态 渲染画面 是否胜利? 显示胜利信息 进入下一关 结束游戏

四、分步实现教程 🛠️

1. 初始化游戏环境

首先,我们需要设置游戏窗口和基本参数:

matlab 复制代码
function sokoban()
    % 清除工作区
    clc; clear; close all;
    
    % 创建图形窗口
    fig = figure('Name', 'MATLAB推箱子游戏', ...
                 'NumberTitle', 'off', ...
                 'MenuBar', 'none', ...
                 'Color', [0.9 0.9 0.9], ...
                 'Position', [100, 100, 800, 600], ...
                 'KeyPressFcn', @keyPressHandler);
    
    % 设置键盘回调函数
    set(fig, 'WindowKeyPressFcn', @keyPressHandler);
    
    % 初始化游戏参数
    game.currentLevel = 1;
    game.moves = 0;
    game.isPlaying = true;
    game.musicOn = true;
    game.gifFrames = {};
    
    % 加载背景音乐
    [y, Fs] = audioread('background.mp3');  % 请准备一个MP3文件
    game.audioPlayer = audioplayer(y, Fs);
    
    % 定义关卡
    game.levels = initializeLevels();
    
    % 开始游戏
    startGame(game, fig);
end

2. 关卡设计

我们需要设计几个关卡来增加游戏趣味性:

matlab 复制代码
function levels = initializeLevels()
    % 关卡1
    levels{1}.map = [
        1 1 1 1 1 1 1 1;
        1 0 0 0 0 0 0 1;
        1 0 0 2 3 0 0 1;
        1 0 4 0 0 0 0 1;
        1 0 0 0 0 0 0 1;
        1 1 1 1 1 1 1 1;
    ];
    
    % 关卡2
    levels{2}.map = [
        1 1 1 1 1 1 1 1;
        1 0 0 0 0 0 0 1;
        1 0 2 0 3 0 0 1;
        1 0 4 0 2 0 0 1;
        1 0 0 0 3 0 0 1;
        1 1 1 1 1 1 1 1;
    ];
    
    % 关卡3 - 更具挑战性
    levels{3}.map = [
        1 1 1 1 1 1 1 1 1;
        1 0 0 0 0 0 0 0 1;
        1 0 2 2 3 3 0 0 1;
        1 0 2 0 3 0 0 0 1;
        1 0 4 0 0 0 0 0 1;
        1 0 0 0 0 0 0 0 1;
        1 1 1 1 1 1 1 1 1;
    ];
end

3. 游戏主循环

这是游戏的核心部分,负责处理输入和更新游戏状态:

matlab 复制代码
function startGame(game, fig)
    % 播放背景音乐
    if game.musicOn
        play(game.audioPlayer);
    end
    
    % 加载当前关卡
    currentMap = game.levels{game.currentLevel}.map;
    
    % 创建游戏界面
    ax = axes('Parent', fig, ...
              'Position', [0.1 0.1 0.8 0.8], ...
              'XTick', [], 'YTick', [], ...
              'XLim', [0 size(currentMap, 2)+1], ...
              'YLim', [0 size(currentMap, 1)+1], ...
              'YDir', 'reverse');
    axis equal;
    hold on;
    
    % 游戏主循环
    while game.isPlaying
        % 渲染游戏地图
        renderMap(currentMap, ax);
        
        % 捕获当前帧用于GIF
        frame = getframe(fig);
        game.gifFrames{end+1} = frame2im(frame);
        
        % 检查胜利条件
        if checkWinCondition(currentMap)
            msg = sprintf('恭喜!你通过了第%d关!', game.currentLevel);
            uiwait(msgbox(msg, '胜利', 'modal'));
            
            % 进入下一关或结束游戏
            if game.currentLevel < length(game.levels)
                game.currentLevel = game.currentLevel + 1;
                currentMap = game.levels{game.currentLevel}.map;
            else
                game.isPlaying = false;
                msgbox('太棒了!你完成了所有关卡!', '游戏结束');
            end
        end
        
        % 暂停一下避免CPU占用过高
        pause(0.05);
    end
    
    % 保存GIF动画
    saveGameGIF(game.gifFrames);
    
    % 停止背景音乐
    stop(game.audioPlayer);
end

4. 渲染游戏地图

这部分代码负责将数字矩阵转换为可视化的游戏界面:

matlab 复制代码
function renderMap(map, ax)
    % 定义颜色映射
    colors = [
        0.7 0.7 0.7;   % 空地 - 灰色
        0.3 0.3 0.3;   % 墙壁 - 深灰色
        0.8 0.5 0.2;   % 箱子 - 橙色
        0.2 0.8 0.2;   % 目标点 - 绿色
        0.2 0.2 0.8;   % 角色 - 蓝色
        0.8 0.8 0.2;   % 箱子在目标点上 - 黄色
        0.2 0.8 0.8;   % 角色在目标点上 - 青色
    ];
    
    % 创建RGB图像
    [rows, cols] = size(map);
    rgbImage = zeros(rows, cols, 3);
    
    for r = 1:rows
        for c = 1:cols
            rgbImage(r, c, :) = colors(map(r, c)+1, :);
        end
    end
    
    % 显示图像
    imshow(rgbImage, 'Parent', ax);
    
    % 添加网格线
    hold on;
    for x = 0.5:cols+0.5
        plot([x x], [0.5 rows+0.5], 'k', 'LineWidth', 1);
    end
    for y = 0.5:rows+0.5
        plot([0.5 cols+0.5], [y y], 'k', 'LineWidth', 1);
    end
    hold off;
    
    % 添加标题和移动计数
    title(ax, sprintf('推箱子游戏 - 第%d关', currentLevel), 'FontSize', 14);
    xlabel(ax, sprintf('移动次数: %d', game.moves), 'FontSize', 12);
end

5. 键盘控制处理

处理玩家的键盘输入:

matlab 复制代码
function keyPressHandler(src, event)
    global game currentMap;
    
    % 获取角色位置
    [playerRow, playerCol] = find(currentMap == 4 | currentMap == 6);
    
    % 根据按键处理移动
    switch event.Key
        case 'uparrow'
            movePlayer(-1, 0);
        case 'downarrow'
            movePlayer(1, 0);
        case 'leftarrow'
            movePlayer(0, -1);
        case 'rightarrow'
            movePlayer(0, 1);
        case 'r'  % 重置关卡
            currentMap = game.levels{game.currentLevel}.map;
        case 'm'  % 切换音乐
            game.musicOn = ~game.musicOn;
            if game.musicOn
                play(game.audioPlayer);
            else
                stop(game.audioPlayer);
            end
        case 'escape'  % 退出游戏
            game.isPlaying = false;
    end
    
    % 嵌套函数处理实际移动逻辑
    function movePlayer(dRow, dCol)
        newRow = playerRow + dRow;
        newCol = playerCol + dCol;
        
        % 检查边界
        if newRow < 1 || newRow > size(currentMap, 1) || ...
           newCol < 1 || newCol > size(currentMap, 2)
            return;
        end
        
        % 检查目标位置
        target = currentMap(newRow, newCol);
        
        % 空地或目标点
        if target == 0 || target == 3
            % 移动角色
            if currentMap(playerRow, playerCol) == 4  % 从空地移动
                currentMap(playerRow, playerCol) = 0;
            else  % 从目标点移动
                currentMap(playerRow, playerCol) = 3;
            end
            
            if target == 0  % 移动到空地
                currentMap(newRow, newCol) = 4;
            else  % 移动到目标点
                currentMap(newRow, newCol) = 6;
            end
            
            game.moves = game.moves + 1;
            
        % 箱子或箱子在目标点上
        elseif target == 2 || target == 5
            % 检查箱子后面是否有空间
            boxNewRow = newRow + dRow;
            boxNewCol = newCol + dCol;
            
            if boxNewRow < 1 || boxNewRow > size(currentMap, 1) || ...
               boxNewCol < 1 || boxNewCol > size(currentMap, 2)
                return;
            end
            
            boxTarget = currentMap(boxNewRow, boxNewCol);
            
            % 箱子后面是空地或目标点
            if boxTarget == 0 || boxTarget == 3
                % 移动箱子
                if target == 2  % 普通箱子
                    if boxTarget == 0  % 推到空地
                        currentMap(boxNewRow, boxNewCol) = 2;
                    else  % 推到目标点
                        currentMap(boxNewRow, boxNewCol) = 5;
                    end
                else  % 箱子在目标点上
                    if boxTarget == 0  % 推到空地
                        currentMap(boxNewRow, boxNewCol) = 2;
                    else  % 推到目标点
                        currentMap(boxNewRow, boxNewCol) = 5;
                    end
                end
                
                % 移动角色
                if currentMap(playerRow, playerCol) == 4  % 从空地移动
                    currentMap(playerRow, playerCol) = 0;
                else  % 从目标点移动
                    currentMap(playerRow, playerCol) = 3;
                end
                
                if target == 2  % 推普通箱子
                    currentMap(newRow, newCol) = 4;
                else  % 推在目标点上的箱子
                    currentMap(newRow, newCol) = 6;
                end
                
                game.moves = game.moves + 1;
            end
        end
    end
end

6. 胜利条件检查

检查是否所有箱子都位于目标点上:

matlab 复制代码
function win = checkWinCondition(map)
    % 检查是否还有普通箱子(2)存在
    win = isempty(find(map == 2, 1));
end

7. 保存游戏GIF

将游戏过程保存为GIF动画:

matlab 复制代码
function saveGameGIF(frames)
    filename = 'sokoban_gameplay.gif';
    
    for idx = 1:length(frames)
        [A, map] = rgb2ind(frames{idx}, 256);
        
        if idx == 1
            imwrite(A, map, filename, 'gif', 'LoopCount', Inf, 'DelayTime', 0.1);
        else
            imwrite(A, map, filename, 'gif', 'WriteMode', 'append', 'DelayTime', 0.1);
        end
    end
    
    fprintf('游戏动画已保存为 %s\n', filename);
end

五、完整可运行代码 🏗️

将所有部分组合起来,以下是完整的推箱子游戏MATLAB实现:

matlab 复制代码
function sokoban()
    % 清除工作区
    clc; clear; close all;
    
    % 创建全局变量
    global game currentMap;
    
    % 创建图形窗口
    fig = figure('Name', 'MATLAB推箱子游戏', ...
                 'NumberTitle', 'off', ...
                 'MenuBar', 'none', ...
                 'Color', [0.9 0.9 0.9], ...
                 'Position', [100, 100, 800, 600]);
    
    % 初始化游戏参数
    game.currentLevel = 1;
    game.moves = 0;
    game.isPlaying = true;
    game.musicOn = true;
    game.gifFrames = {};
    
    % 尝试加载背景音乐
    try
        [y, Fs] = audioread('background.mp3');
        game.audioPlayer = audioplayer(y, Fs);
    catch
        warning('无法加载背景音乐文件,游戏将继续但没有声音');
        game.musicOn = false;
    end
    
    % 定义关卡
    game.levels = initializeLevels();
    
    % 设置键盘回调函数
    set(fig, 'WindowKeyPressFcn', @keyPressHandler);
    
    % 开始游戏
    startGame(game, fig);
end

function levels = initializeLevels()
    % 关卡1
    levels{1}.map = [
        1 1 1 1 1 1 1 1;
        1 0 0 0 0 0 0 1;
        1 0 0 2 3 0 0 1;
        1 0 4 0 0 0 0 1;
        1 0 0 0 0 0 0 1;
        1 1 1 1 1 1 1 1;
    ];
    
    % 关卡2
    levels{2}.map = [
        1 1 1 1 1 1 1 1;
        1 0 0 0 0 0 0 1;
        1 0 2 0 3 0 0 1;
        1 0 4 0 2 0 0 1;
        1 0 0 0 3 0 0 1;
        1 1 1 1 1 1 1 1;
    ];
    
    % 关卡3
    levels{3}.map = [
        1 1 1 1 1 1 1 1 1;
        1 0 0 0 0 0 0 0 1;
        1 0 2 2 3 3 0 0 1;
        1 0 2 0 3 0 0 0 1;
        1 0 4 0 0 0 0 0 1;
        1 0 0 0 0 0 0 0 1;
        1 1 1 1 1 1 1 1 1;
    ];
end

function startGame(game, fig)
    global currentMap;
    
    % 播放背景音乐
    if game.musicOn && isfield(game, 'audioPlayer')
        play(game.audioPlayer);
    end
    
    % 加载当前关卡
    currentMap = game.levels{game.currentLevel}.map;
    
    % 创建游戏界面
    ax = axes('Parent', fig, ...
              'Position', [0.1 0.1 0.8 0.8], ...
              'XTick', [], 'YTick', [], ...
              'XLim', [0 size(currentMap, 2)+1], ...
              'YLim', [0 size(currentMap, 1)+1], ...
              'YDir', 'reverse');
    axis equal;
    hold on;
    
    % 游戏主循环
    while game.isPlaying
        % 渲染游戏地图
        renderMap(currentMap, ax);
        
        % 捕获当前帧用于GIF
        frame = getframe(fig);
        game.gifFrames{end+1} = frame2im(frame);
        
        % 检查胜利条件
        if checkWinCondition(currentMap)
            msg = sprintf('恭喜!你通过了第%d关!', game.currentLevel);
            uiwait(msgbox(msg, '胜利', 'modal'));
            
            % 进入下一关或结束游戏
            if game.currentLevel < length(game.levels)
                game.currentLevel = game.currentLevel + 1;
                currentMap = game.levels{game.currentLevel}.map;
                game.moves = 0;
            else
                game.isPlaying = false;
                msgbox('太棒了!你完成了所有关卡!', '游戏结束');
            end
        end
        
        % 暂停一下避免CPU占用过高
        pause(0.05);
    end
    
    % 保存GIF动画
    if ~isempty(game.gifFrames)
        saveGameGIF(game.gifFrames);
    end
    
    % 停止背景音乐
    if game.musicOn && isfield(game, 'audioPlayer')
        stop(game.audioPlayer);
    end
end

function renderMap(map, ax)
    global game;
    
    % 定义颜色映射
    colors = [
        0.7 0.7 0.7;   % 空地 - 灰色
        0.3 0.3 0.3;   % 墙壁 - 深灰色
        0.8 0.5 0.2;   % 箱子 - 橙色
        0.2 0.8 0.2;   % 目标点 - 绿色
        0.2 0.2 0.8;   % 角色 - 蓝色
        0.8 0.8 0.2;   % 箱子在目标点上 - 黄色
        0.2 0.8 0.8;   % 角色在目标点上 - 青色
    ];
    
    % 创建RGB图像
    [rows, cols] = size(map);
    rgbImage = zeros(rows, cols, 3);
    
    for r = 1:rows
        for c = 1:cols
            rgbImage(r, c, :) = colors(map(r, c)+1, :);
        end
    end
    
    % 显示图像
    imshow(rgbImage, 'Parent', ax);
    
    % 添加网格线
    hold on;
    for x = 0.5:cols+0.5
        plot([x x], [0.5 rows+0.5], 'k', 'LineWidth', 1);
    end
    for y = 0.5:rows+0.5
        plot([0.5 cols+0.5], [y y], 'k', 'LineWidth', 1);
    end
    hold off;
    
    % 添加标题和移动计数
    title(ax, sprintf('推箱子游戏 - 第%d关', game.currentLevel), 'FontSize', 14);
    xlabel(ax, sprintf('移动次数: %d', game.moves), 'FontSize', 12);
end

function keyPressHandler(src, event)
    global game currentMap;
    
    % 获取角色位置
    [playerRow, playerCol] = find(currentMap == 4 | currentMap == 6);
    if isempty(playerRow) || isempty(playerCol)
        return;
    end
    
    % 根据按键处理移动
    switch event.Key
        case 'uparrow'
            movePlayer(-1, 0);
        case 'downarrow'
            movePlayer(1, 0);
        case 'leftarrow'
            movePlayer(0, -1);
        case 'rightarrow'
            movePlayer(0, 1);
        case 'r'  % 重置关卡
            currentMap = game.levels{game.currentLevel}.map;
            game.moves = 0;
        case 'm'  % 切换音乐
            if isfield(game, 'audioPlayer')
                game.musicOn = ~game.musicOn;
                if game.musicOn
                    play(game.audioPlayer);
                else
                    stop(game.audioPlayer);
                end
            end
        case 'escape'  % 退出游戏
            game.isPlaying = false;
    end
    
    % 嵌套函数处理实际移动逻辑
    function movePlayer(dRow, dCol)
        newRow = playerRow + dRow;
        newCol = playerCol + dCol;
        
        % 检查边界
        if newRow < 1 || newRow > size(currentMap, 1) || ...
           newCol < 1 || newCol > size(currentMap, 2)
            return;
        end
        
        % 检查目标位置
        target = currentMap(newRow, newCol);
        
        % 空地或目标点
        if target == 0 || target == 3
            % 移动角色
            if currentMap(playerRow, playerCol) == 4  % 从空地移动
                currentMap(playerRow, playerCol) = 0;
            else  % 从目标点移动
                currentMap(playerRow, playerCol) = 3;
            end
            
            if target == 0  % 移动到空地
                currentMap(newRow, newCol) = 4;
            else  % 移动到目标点
                currentMap(newRow, newCol) = 6;
            end
            
            game.moves = game.moves + 1;
            
        % 箱子或箱子在目标点上
        elseif target == 2 || target == 5
            % 检查箱子后面是否有空间
            boxNewRow = newRow + dRow;
            boxNewCol = newCol + dCol;
            
            if boxNewRow < 1 || boxNewRow > size(currentMap, 1) || ...
               boxNewCol < 1 || boxNewCol > size(currentMap, 2)
                return;
            end
            
            boxTarget = currentMap(boxNewRow, boxNewCol);
            
            % 箱子后面是空地或目标点
            if boxTarget == 0 || boxTarget == 3
                % 移动箱子
                if target == 2  % 普通箱子
                    if boxTarget == 0  % 推到空地
                        currentMap(boxNewRow, boxNewCol) = 2;
                    else  % 推到目标点
                        currentMap(boxNewRow, boxNewCol) = 5;
                    end
                else  % 箱子在目标点上
                    if boxTarget == 0  % 推到空地
                        currentMap(boxNewRow, boxNewCol) = 2;
                    else  % 推到目标点
                        currentMap(boxNewRow, boxNewCol) = 5;
                    end
                end
                
                % 移动角色
                if currentMap(playerRow, playerCol) == 4  % 从空地移动
                    currentMap(playerRow, playerCol) = 0;
                else  % 从目标点移动
                    currentMap(playerRow, playerCol) = 3;
                end
                
                if target == 2  % 推普通箱子
                    currentMap(newRow, newCol) = 4;
                else  % 推在目标点上的箱子
                    currentMap(newRow, newCol) = 6;
                end
                
                game.moves = game.moves + 1;
            end
        end
    end
end

function win = checkWinCondition(map)
    % 检查是否还有普通箱子(2)存在
    win = isempty(find(map == 2, 1));
end

function saveGameGIF(frames)
    filename = 'sokoban_gameplay.gif';
    
    for idx = 1:length(frames)
        [A, map] = rgb2ind(frames{idx}, 256);
        
        if idx == 1
            imwrite(A, map, filename, 'gif', 'LoopCount', Inf, 'DelayTime', 0.1);
        else
            imwrite(A, map, filename, 'gif', 'WriteMode', 'append', 'DelayTime', 0.1);
        end
    end
    
    fprintf('游戏动画已保存为 %s\n', filename);
end

六、游戏运行与扩展建议 🚀

如何运行游戏

  1. 将上述完整代码保存为sokoban.m文件
  2. 准备一个名为background.mp3的背景音乐文件放在同一目录下
  3. 在MATLAB命令窗口输入sokoban运行游戏

游戏操作说明

  • 方向键:控制角色移动
  • R键:重置当前关卡
  • M键:切换背景音乐开关
  • ESC键:退出游戏

以下是我玩了一局的效果,献丑了,哈哈~

  • 红色块:箱子
  • 蓝色块:人
  • 绿色块 :目标位置

扩展建议

想要进一步提升这个游戏?可以考虑:

  1. 添加更多关卡:设计更具挑战性的地图
  2. 增加音效:为推箱子、胜利等动作添加音效
  3. 添加计时功能:记录完成关卡所用的时间
  4. 实现撤销功能:允许玩家撤销上一步操作
  5. 添加关卡编辑器:让玩家可以自己设计关卡

七、总结与收获 📚

通过这个项目,我们学习了:

  • MATLAB图形界面编程
  • 游戏状态管理和更新
  • 键盘事件处理
  • GIF动画生成
  • 音频播放控制

希望这个推箱子游戏项目能带给你乐趣和知识!尝试修改代码,添加你自己的创意吧!🎉

Happy coding! 👨💻👩💻

相关推荐
买了一束花3 分钟前
数据预处理之数据平滑处理详解
开发语言·人工智能·算法·matlab
秭霏鱼4 分钟前
Python+大模型 day01
开发语言·python
破晓的历程6 分钟前
Qt之Qfile类
开发语言·qt
纸包鱼最好吃27 分钟前
java基础-package关键字、MVC、import关键字
java·开发语言·mvc
njsgcs27 分钟前
opencascade.js stp vite webpack 调试笔记
开发语言·前端·javascript
PgSheep33 分钟前
Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案
java·开发语言
林鸿群36 分钟前
go语言实现IP归属地查询
开发语言·golang·ip归属地
学地理的小胖砸44 分钟前
【Python 异常处理】
开发语言·python
程序员拂雨1 小时前
Java知识框架
java·开发语言
水水沝淼㵘2 小时前
嵌入式开发学习日志(数据结构--单链表)Day20
c语言·开发语言·数据结构·学习·算法