《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-《打砖块:向量反射与实时物理模拟》MATLAB教程

《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-《打砖块:向量反射与实时物理模拟》MATLAB教程 🎮

文章目录

  • [《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-《打砖块:向量反射与实时物理模拟》MATLAB教程 🎮](#《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-《打砖块:向量反射与实时物理模拟》MATLAB教程 🎮)
    • 引言:从游戏到物理模拟
    • [1. 设计思路与游戏概述 🧠](#1. 设计思路与游戏概述 🧠)
      • [1.1 物理模型关键点 ⚛️](#1.1 物理模型关键点 ⚛️)
    • [2. 核心原理详解 🔍](#2. 核心原理详解 🔍)
      • [2.1 向量反射原理 📐](#2.1 向量反射原理 📐)
      • [2.2 碰撞检测原理 🔄](#2.2 碰撞检测原理 🔄)
      • [2.3 游戏循环结构 🔁](#2.3 游戏循环结构 🔁)
    • [3. 完整流程图 📊](#3. 完整流程图 📊)
    • [4. 分步实现教程 🛠️](#4. 分步实现教程 🛠️)
      • [4.1 初始化设置](#4.1 初始化设置)
      • [4.2 创建游戏对象](#4.2 创建游戏对象)
      • [4.3 游戏主循环](#4.3 游戏主循环)
      • [4.4 碰撞检测函数](#4.4 碰撞检测函数)
    • [5. 完整可运行代码 🏆](#5. 完整可运行代码 🏆)
    • [6. 游戏操作说明 🎮](#6. 游戏操作说明 🎮)
    • [7. 扩展思路 💡](#7. 扩展思路 💡)

引言:从游戏到物理模拟

还记得小时候玩过的打砖块游戏吗?🎯 一个小球在屏幕上弹来弹去,击碎各种砖块,同时玩家控制一块挡板防止小球掉落。这看似简单的游戏背后,其实蕴含着丰富的物理和数学原理!今天,我们就用MATLAB来实现这个经典游戏,并深入探讨其中的向量反射和实时物理模拟技术。

通过本教程,你将学到:

  • 向量运算在游戏物理中的应用
  • 实时碰撞检测的实现方法
  • MATLAB动画和交互式图形编程
  • 游戏状态管理和逻辑控制

准备好你的MATLAB环境(2016b版本),让我们开始这场有趣的编程之旅吧!🚀

1. 设计思路与游戏概述 🧠

打砖块游戏(Breakout/Arkanoid)是经典的街机游戏,包含以下核心元素:

  • 一个由玩家控制的挡板(paddle)
  • 一个运动的(ball)
  • 由砖块组成的(bricks)
  • 边界(walls)

游戏目标是用挡板反弹球,消除所有砖块。每次球碰到砖块,砖块消失,玩家得分。

1.1 物理模型关键点 ⚛️

  1. 碰撞检测:判断球与边界、挡板、砖块的接触
  2. 反射计算:球碰到物体后的运动方向变化
  3. 实时渲染:游戏画面的流畅更新
  4. 游戏逻辑:得分、生命、胜负判断

2. 核心原理详解 🔍

2.1 向量反射原理 📐

球碰到平面后的反射遵循"入射角=反射角"原则。数学上可以用向量运算表示:

给定:

  • 入射向量 v = [vx, vy]
  • 法向量 n = [nx, ny] (垂直于反射平面)

反射向量 r 的计算公式为:
r = v − 2 ( v ⋅ n ) n r = v - 2(v \cdot n)n r=v−2(v⋅n)n

在MATLAB中实现:

matlab 复制代码
function reflectedVec = reflectVector(inVec, normalVec)
    % 归一化法向量
    normalVec = normalVec / norm(normalVec);
    % 计算反射向量
    reflectedVec = inVec - 2 * dot(inVec, normalVec) * normalVec;
end

2.2 碰撞检测原理 🔄

我们需要检测球与以下物体的碰撞:

  1. 边界:比较球心坐标与边界位置
  2. 挡板:判断球是否在挡板的矩形区域内
  3. 砖块:类似挡板,但需要考虑砖块是否已被消除

2.3 游戏循环结构 🔁

标准游戏循环包含以下步骤:

  1. 初始化:创建游戏对象和变量
  2. 输入处理:读取玩家操作
  3. 状态更新:计算物理和游戏逻辑
  4. 渲染:绘制游戏画面
  5. 循环控制:控制帧率和退出条件

3. 完整流程图 📊

是 否 开始 初始化游戏 绘制初始场景 游戏进行中? 处理玩家输入 更新球位置 检测碰撞 处理碰撞响应 更新游戏状态 绘制新帧 显示游戏结果 结束

4. 分步实现教程 🛠️

4.1 初始化设置

matlab 复制代码
function breakoutGame()
    % 清除工作区和图形窗口
    clc; clear; close all;
    
    % 游戏参数设置
    gameParams = struct(...
        'paddleWidth', 100, ...    % 挡板宽度
        'paddleHeight', 15, ...    % 挡板高度
        'ballRadius', 10, ...      % 球半径
        'brickRows', 5, ...        % 砖块行数
        'brickCols', 10, ...       % 砖块列数
        'brickWidth', 60, ...      % 砖块宽度
        'brickHeight', 20, ...     % 砖块高度
        'brickOffsetTop', 50, ...  % 砖块顶部偏移
        'brickPadding', 5, ...     % 砖块间距
        'ballSpeed', 8, ...        % 球初始速度
        'lives', 3, ...            % 初始生命数
        'score', 0 ...             % 初始得分
    );
    
    % 创建图形窗口
    fig = figure('Name', 'MATLAB打砖块', ...
                 'NumberTitle', 'off', ...
                 'Position', [100, 100, 800, 600], ...
                 'KeyPressFcn', @keyDown, ...
                 'KeyReleaseFcn', @keyUp, ...
                 'WindowButtonDownFcn', @mouseClick);
    
    % 创建坐标轴
    ax = axes('Parent', fig, ...
              'Position', [0.05, 0.05, 0.9, 0.9], ...
              'XLim', [0, 800], ...
              'YLim', [0, 600], ...
              'Color', [0.1, 0.1, 0.3], ...
              'XTick', [], ...
              'YTick', []);
    hold(ax, 'on');
    axis equal;

4.2 创建游戏对象

matlab 复制代码
    % 创建挡板
    paddle = rectangle('Parent', ax, ...
                      'Position', [350, 30, gameParams.paddleWidth, gameParams.paddleHeight], ...
                      'FaceColor', [0.8, 0.2, 0.2], ...
                      'EdgeColor', 'none', ...
                      'Curvature', [0.2, 0.2]);
    
    % 创建球
    theta = rand * 2 * pi;  % 随机初始角度
    ballVel = [gameParams.ballSpeed * cos(theta), gameParams.ballSpeed * sin(theta)];
    ball = rectangle('Parent', ax, ...
                    'Position', [400, 200, gameParams.ballRadius*2, gameParams.ballRadius*2], ...
                    'FaceColor', [0.9, 0.9, 0.1], ...
                    'EdgeColor', 'none', ...
                    'Curvature', [1, 1]);
    
    % 创建砖块
    bricks = gobjects(gameParams.brickRows, gameParams.brickCols);
    brickColors = hsv(gameParams.brickRows);  % 每行不同颜色
    
    for r = 1:gameParams.brickRows
        for c = 1:gameParams.brickCols
            brickX = (c-1) * (gameParams.brickWidth + gameParams.brickPadding);
            brickY = 550 - (r-1) * (gameParams.brickHeight + gameParams.brickPadding);
            bricks(r,c) = rectangle('Parent', ax, ...
                                   'Position', [brickX, brickY, gameParams.brickWidth, gameParams.brickHeight], ...
                                   'FaceColor', brickColors(r,:), ...
                                   'EdgeColor', 'w');
        end
    end
    
    % 创建文本显示
    scoreText = text(ax, 20, 580, sprintf('得分: %d', gameParams.score), ...
                    'Color', 'w', 'FontSize', 12);
    livesText = text(ax, 700, 580, sprintf('生命: %d', gameParams.lives), ...
                    'Color', 'w', 'FontSize', 12);
    startText = text(ax, 400, 300, '点击开始游戏', ...
                    'Color', 'w', 'FontSize', 24, ...
                    'HorizontalAlignment', 'center');

4.3 游戏主循环

matlab 复制代码
    % 游戏状态变量
    gameState = struct(...
        'isRunning', false, ...    % 游戏是否进行中
        'paddleDir', 0, ...        % 挡板移动方向 (-1:左, 0:停止, 1:右)
        'paddleSpeed', 15, ...     % 挡板移动速度
        'activeBricks', true(gameParams.brickRows, gameParams.brickCols) ... % 活跃砖块
    );
    
    % 键盘控制回调函数
    function keyDown(~, event)
        switch event.Key
            case 'leftarrow'
                gameState.paddleDir = -1;
            case 'rightarrow'
                gameState.paddleDir = 1;
            case 'escape'
                gameState.isRunning = false;
        end
    end
    
    function keyUp(~, event)
        switch event.Key
            case {'leftarrow', 'rightarrow'}
                gameState.paddleDir = 0;
        end
    end
    
    % 鼠标点击开始游戏
    function mouseClick(~, ~)
        if ~gameState.isRunning
            gameState.isRunning = true;
            delete(startText);
            startText = [];
        end
    end
    
    % 主游戏循环
    while ishandle(fig)
        if gameState.isRunning
            % 更新挡板位置
            paddlePos = get(paddle, 'Position');
            newX = paddlePos(1) + gameState.paddleSpeed * gameState.paddleDir;
            
            % 限制挡板不超出边界
            newX = max(0, min(newX, 800 - gameParams.paddleWidth));
            set(paddle, 'Position', [newX, paddlePos(2), paddlePos(3), paddlePos(4)]);
            
            % 更新球位置
            ballPos = get(ball, 'Position');
            newBallX = ballPos(1) + ballVel(1);
            newBallY = ballPos(2) + ballVel(2);
            
            % 检测碰撞
            [ballVel, gameParams, gameState] = checkCollisions(...
                [newBallX, newBallY], ballVel, gameParams, gameState, paddle, bricks);
            
            % 更新球位置
            set(ball, 'Position', [newBallX, newBallY, ballPos(3), ballPos(4)]);
            
            % 更新文本显示
            set(scoreText, 'String', sprintf('得分: %d', gameParams.score));
            set(livesText, 'String', sprintf('生命: %d', gameParams.lives));
            
            % 检查游戏结束条件
            if newBallY < 0  % 球落到底部
                gameParams.lives = gameParams.lives - 1;
                if gameParams.lives <= 0
                    gameState.isRunning = false;
                    text(ax, 400, 300, '游戏结束!', ...
                        'Color', 'r', 'FontSize', 36, ...
                        'HorizontalAlignment', 'center');
                else
                    % 重置球位置
                    set(ball, 'Position', [400, 200, gameParams.ballRadius*2, gameParams.ballRadius*2]);
                    theta = rand * 2 * pi;
                    ballVel = [gameParams.ballSpeed * cos(theta), gameParams.ballSpeed * sin(theta)];
                    pause(1);
                end
            end
            
            % 检查胜利条件
            if ~any(gameState.activeBricks(:))
                gameState.isRunning = false;
                text(ax, 400, 300, '恭喜通关!', ...
                    'Color', 'g', 'FontSize', 36, ...
                    'HorizontalAlignment', 'center');
            end
        end
        
        % 控制帧率
        pause(0.02);
    end

4.4 碰撞检测函数

matlab 复制代码
function [newVel, gameParams, gameState] = checkCollisions(ballPos, ballVel, gameParams, gameState, paddle, bricks)
    % 获取球参数
    ballX = ballPos(1) + gameParams.ballRadius;
    ballY = ballPos(2) + gameParams.ballRadius;
    
    % 边界碰撞检测
    if ballX <= gameParams.ballRadius || ballX >= 800 - gameParams.ballRadius
        ballVel(1) = -ballVel(1);  % 水平反转
    end
    if ballY >= 600 - gameParams.ballRadius
        ballVel(2) = -ballVel(2);  % 垂直反转
    end
    
    % 挡板碰撞检测
    paddlePos = get(paddle, 'Position');
    if ballY <= paddlePos(2) + paddlePos(4) + gameParams.ballRadius && ...
       ballY >= paddlePos(2) && ...
       ballX >= paddlePos(1) - gameParams.ballRadius && ...
       ballX <= paddlePos(1) + paddlePos(3) + gameParams.ballRadius
        
        % 计算碰撞点相对于挡板中心的位置 (-1到1)
        hitPos = (ballX - (paddlePos(1) + paddlePos(3)/2)) / (paddlePos(3)/2);
        
        % 根据碰撞点调整反射角度
        maxAngle = pi/3;  % 最大反射角度 (60度)
        angle = hitPos * maxAngle;
        
        % 计算新速度向量
        speed = norm(ballVel);
        ballVel = [speed * sin(angle), speed * cos(angle)];
        
        % 增加一点速度让游戏更有挑战性
        ballVel = ballVel * 1.02;
    end
    
    % 砖块碰撞检测
    for r = 1:gameParams.brickRows
        for c = 1:gameParams.brickCols
            if gameState.activeBricks(r,c)
                brickPos = get(bricks(r,c), 'Position');
                
                % 检查球是否与砖块相交
                if ballX + gameParams.ballRadius > brickPos(1) && ...
                   ballX - gameParams.ballRadius < brickPos(1) + brickPos(3) && ...
                   ballY + gameParams.ballRadius > brickPos(2) && ...
                   ballY - gameParams.ballRadius < brickPos(2) + brickPos(4)
                    
                    % 确定碰撞边 (简化版)
                    if ballY < brickPos(2) || ballY > brickPos(2) + brickPos(4)
                        ballVel(2) = -ballVel(2);  % 上下碰撞
                    else
                        ballVel(1) = -ballVel(1);  % 左右碰撞
                    end
                    
                    % 标记砖块为不活跃并隐藏
                    gameState.activeBricks(r,c) = false;
                    set(bricks(r,c), 'Visible', 'off');
                    
                    % 增加分数
                    gameParams.score = gameParams.score + 10;
                    
                    % 只需要处理一次碰撞
                    break;
                end
            end
        end
    end
    
    newVel = ballVel;
end

5. 完整可运行代码 🏆

将以下所有代码段按顺序组合成一个.m文件即可运行:

matlab 复制代码
function breakoutGame()
    % 清除工作区和图形窗口
    clc; clear; close all;
    
    % 游戏参数设置
    gameParams = struct(...
        'paddleWidth', 100, ...    % 挡板宽度
        'paddleHeight', 15, ...    % 挡板高度
        'ballRadius', 10, ...      % 球半径
        'brickRows', 5, ...        % 砖块行数
        'brickCols', 10, ...       % 砖块列数
        'brickWidth', 60, ...      % 砖块宽度
        'brickHeight', 20, ...     % 砖块高度
        'brickOffsetTop', 50, ...  % 砖块顶部偏移
        'brickPadding', 5, ...     % 砖块间距
        'ballSpeed', 8, ...        % 球初始速度
        'lives', 3, ...            % 初始生命数
        'score', 0 ...             % 初始得分
    );
    
    % 创建图形窗口
    fig = figure('Name', 'MATLAB打砖块', ...
                 'NumberTitle', 'off', ...
                 'Position', [100, 100, 800, 600], ...
                 'KeyPressFcn', @keyDown, ...
                 'KeyReleaseFcn', @keyUp, ...
                 'WindowButtonDownFcn', @mouseClick);
    
    % 创建坐标轴
    ax = axes('Parent', fig, ...
              'Position', [0.05, 0.05, 0.9, 0.9], ...
              'XLim', [0, 800], ...
              'YLim', [0, 600], ...
              'Color', [0.1, 0.1, 0.3], ...
              'XTick', [], ...
              'YTick', []);
    hold(ax, 'on');
    axis equal;
    
    % 创建挡板
    paddle = rectangle('Parent', ax, ...
                      'Position', [350, 30, gameParams.paddleWidth, gameParams.paddleHeight], ...
                      'FaceColor', [0.8, 0.2, 0.2], ...
                      'EdgeColor', 'none', ...
                      'Curvature', [0.2, 0.2]);
    
    % 创建球
    theta = rand * 2 * pi;  % 随机初始角度
    ballVel = [gameParams.ballSpeed * cos(theta), gameParams.ballSpeed * sin(theta)];
    ball = rectangle('Parent', ax, ...
                    'Position', [400, 200, gameParams.ballRadius*2, gameParams.ballRadius*2], ...
                    'FaceColor', [0.9, 0.9, 0.1], ...
                    'EdgeColor', 'none', ...
                    'Curvature', [1, 1]);
    
    % 创建砖块
    bricks = gobjects(gameParams.brickRows, gameParams.brickCols);
    brickColors = hsv(gameParams.brickRows);  % 每行不同颜色
    
    for r = 1:gameParams.brickRows
        for c = 1:gameParams.brickCols
            brickX = (c-1) * (gameParams.brickWidth + gameParams.brickPadding);
            brickY = 550 - (r-1) * (gameParams.brickHeight + gameParams.brickPadding);
            bricks(r,c) = rectangle('Parent', ax, ...
                                   'Position', [brickX, brickY, gameParams.brickWidth, gameParams.brickHeight], ...
                                   'FaceColor', brickColors(r,:), ...
                                   'EdgeColor', 'w');
        end
    end
    
    % 创建文本显示
    scoreText = text(ax, 20, 580, sprintf('得分: %d', gameParams.score), ...
                    'Color', 'w', 'FontSize', 12);
    livesText = text(ax, 700, 580, sprintf('生命: %d', gameParams.lives), ...
                    'Color', 'w', 'FontSize', 12);
    startText = text(ax, 400, 300, '点击开始游戏', ...
                    'Color', 'w', 'FontSize', 24, ...
                    'HorizontalAlignment', 'center');
    
    % 游戏状态变量
    gameState = struct(...
        'isRunning', false, ...    % 游戏是否进行中
        'paddleDir', 0, ...        % 挡板移动方向 (-1:左, 0:停止, 1:右)
        'paddleSpeed', 15, ...     % 挡板移动速度
        'activeBricks', true(gameParams.brickRows, gameParams.brickCols) ... % 活跃砖块
    );
    
    % 键盘控制回调函数
    function keyDown(~, event)
        switch event.Key
            case 'leftarrow'
                gameState.paddleDir = -1;
            case 'rightarrow'
                gameState.paddleDir = 1;
            case 'escape'
                gameState.isRunning = false;
        end
    end
    
    function keyUp(~, event)
        switch event.Key
            case {'leftarrow', 'rightarrow'}
                gameState.paddleDir = 0;
        end
    end
    
    % 鼠标点击开始游戏
    function mouseClick(~, ~)
        if ~gameState.isRunning
            gameState.isRunning = true;
            delete(startText);
            startText = [];
        end
    end
    
    % 主游戏循环
    while ishandle(fig)
        if gameState.isRunning
            % 更新挡板位置
            paddlePos = get(paddle, 'Position');
            newX = paddlePos(1) + gameState.paddleSpeed * gameState.paddleDir;
            
            % 限制挡板不超出边界
            newX = max(0, min(newX, 800 - gameParams.paddleWidth));
            set(paddle, 'Position', [newX, paddlePos(2), paddlePos(3), paddlePos(4)]);
            
            % 更新球位置
            ballPos = get(ball, 'Position');
            newBallX = ballPos(1) + ballVel(1);
            newBallY = ballPos(2) + ballVel(2);
            
            % 检测碰撞
            [ballVel, gameParams, gameState] = checkCollisions(...
                [newBallX, newBallY], ballVel, gameParams, gameState, paddle, bricks);
            
            % 更新球位置
            set(ball, 'Position', [newBallX, newBallY, ballPos(3), ballPos(4)]);
            
            % 更新文本显示
            set(scoreText, 'String', sprintf('得分: %d', gameParams.score));
            set(livesText, 'String', sprintf('生命: %d', gameParams.lives));
            
            % 检查游戏结束条件
            if newBallY < 0  % 球落到底部
                gameParams.lives = gameParams.lives - 1;
                if gameParams.lives <= 0
                    gameState.isRunning = false;
                    text(ax, 400, 300, '游戏结束!', ...
                        'Color', 'r', 'FontSize', 36, ...
                        'HorizontalAlignment', 'center');
                else
                    % 重置球位置
                    set(ball, 'Position', [400, 200, gameParams.ballRadius*2, gameParams.ballRadius*2]);
                    theta = rand * 2 * pi;
                    ballVel = [gameParams.ballSpeed * cos(theta), gameParams.ballSpeed * sin(theta)];
                    pause(1);
                end
            end
            
            % 检查胜利条件
            if ~any(gameState.activeBricks(:))
                gameState.isRunning = false;
                text(ax, 400, 300, '恭喜通关!', ...
                    'Color', 'g', 'FontSize', 36, ...
                    'HorizontalAlignment', 'center');
            end
        end
        
        % 控制帧率
        pause(0.02);
    end
    
    % 碰撞检测函数
    function [newVel, gameParams, gameState] = checkCollisions(ballPos, ballVel, gameParams, gameState, paddle, bricks)
        % 获取球参数
        ballX = ballPos(1) + gameParams.ballRadius;
        ballY = ballPos(2) + gameParams.ballRadius;
        
        % 边界碰撞检测
        if ballX <= gameParams.ballRadius || ballX >= 800 - gameParams.ballRadius
            ballVel(1) = -ballVel(1);  % 水平反转
        end
        if ballY >= 600 - gameParams.ballRadius
            ballVel(2) = -ballVel(2);  % 垂直反转
        end
        
        % 挡板碰撞检测
        paddlePos = get(paddle, 'Position');
        if ballY <= paddlePos(2) + paddlePos(4) + gameParams.ballRadius && ...
           ballY >= paddlePos(2) && ...
           ballX >= paddlePos(1) - gameParams.ballRadius && ...
           ballX <= paddlePos(1) + paddlePos(3) + gameParams.ballRadius
            
            % 计算碰撞点相对于挡板中心的位置 (-1到1)
            hitPos = (ballX - (paddlePos(1) + paddlePos(3)/2)) / (paddlePos(3)/2);
            
            % 根据碰撞点调整反射角度
            maxAngle = pi/3;  % 最大反射角度 (60度)
            angle = hitPos * maxAngle;
            
            % 计算新速度向量
            speed = norm(ballVel);
            ballVel = [speed * sin(angle), speed * cos(angle)];
            
            % 增加一点速度让游戏更有挑战性
            ballVel = ballVel * 1.02;
        end
        
        % 砖块碰撞检测
        for r = 1:gameParams.brickRows
            for c = 1:gameParams.brickCols
                if gameState.activeBricks(r,c)
                    brickPos = get(bricks(r,c), 'Position');
                    
                    % 检查球是否与砖块相交
                    if ballX + gameParams.ballRadius > brickPos(1) && ...
                       ballX - gameParams.ballRadius < brickPos(1) + brickPos(3) && ...
                       ballY + gameParams.ballRadius > brickPos(2) && ...
                       ballY - gameParams.ballRadius < brickPos(2) + brickPos(4)
                        
                        % 确定碰撞边 (简化版)
                        if ballY < brickPos(2) || ballY > brickPos(2) + brickPos(4)
                            ballVel(2) = -ballVel(2);  % 上下碰撞
                        else
                            ballVel(1) = -ballVel(1);  % 左右碰撞
                        end
                        
                        % 标记砖块为不活跃并隐藏
                        gameState.activeBricks(r,c) = false;
                        set(bricks(r,c), 'Visible', 'off');
                        
                        % 增加分数
                        gameParams.score = gameParams.score + 10;
                        
                        % 只需要处理一次碰撞
                        break;
                    end
                end
            end
        end
        
        newVel = ballVel;
    end
end

来看看我这个菜鸡玩了一局的效果,屏幕前的你也可以试试看看能的多少分~

6. 游戏操作说明 🎮

  • 左右箭头键:移动挡板
  • ESC键:退出游戏
  • 鼠标点击:开始游戏

7. 扩展思路 💡

如果你想进一步提升这个游戏,可以考虑:

  1. 增加音效 :使用audioplayer添加碰撞音效
  2. 多种砖块:不同颜色砖块需要多次击中才能消除
  3. 特殊道具:球碰到某些砖块会掉落道具,如加长挡板、额外生命等
  4. 关卡设计:设计不同布局的砖块排列
  5. 粒子效果:砖块消除时添加爆炸效果

希望你喜欢这个MATLAB打砖块游戏教程!通过这个项目,你不仅学会了向量反射的原理,还掌握了实时物理模拟和游戏开发的基本技巧。Happy coding! 🚀

相关推荐
大道随心27 分钟前
【wpf】10 C#树形控件高效实现:递归构建与路径查找优化详解
开发语言·c#·wpf
香蕉可乐荷包蛋35 分钟前
Python学习之路(玖)-图像识别的实现
开发语言·python·学习
航Hang*1 小时前
C PRIMER PLUS——第6-2节:二维数组与多维数组
c语言·开发语言·经验分享·程序人生·算法·学习方法·visual studio
易只轻松熊1 小时前
C++(1):整数常量
开发语言·c++
努力的搬砖人.1 小时前
Java 线程池原理
java·开发语言
Dovis(誓平步青云)2 小时前
精讲C++四大核心特性:内联函数加速原理、auto智能推导、范围for循环与空指针进阶
c语言·开发语言·c++·笔记·算法·学习方法
passionSnail2 小时前
《用MATLAB玩转游戏开发》Flappy Bird:小鸟飞行大战MATLAB趣味实现
开发语言·matlab
jz_ddk2 小时前
[学习]RTKLib详解:convkml.c、convrnx.c与geoid.c
c语言·开发语言·学习
stevenzqzq2 小时前
kotlin flow防抖
开发语言·kotlin·flow
极小狐2 小时前
如何从极狐GitLab 容器镜像库中删除容器镜像?
java·linux·开发语言·数据库·python·elasticsearch·gitlab