import { useState } from 'react'; import { motion } from 'framer-motion'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; const carControllerScript = ` -- 車輛控制腳本 -- 常數設定 local FORWARD_FORCE = 15000 -- 前進力量 local REVERSE_FORCE = 8000 -- 後退力量 local TURN_SPEED = 2.5 -- 轉向速度 local MAX_SPEED = 60 -- 最大速度 (studs/秒) local BRAKE_FORCE = 10000 -- 剎車力量 -- 獲取車輛模型及其組件 local car = script.Parent local body = car:WaitForChild("Body") -- 車身主體 local frontLeftWheel = car:WaitForChild("FrontLeftWheel") local frontRightWheel = car:WaitForChild("FrontRightWheel") local rearLeftWheel = car:WaitForChild("RearLeftWheel") local rearRightWheel = car:WaitForChild("RearRightWheel") -- 獲取車輪連接點 local flHinge = car:WaitForChild("FLHinge") local frHinge = car:WaitForChild("FRHinge") local rlHinge = car:WaitForChild("RLHinge") local rrHinge = car:WaitForChild("RRHinge") -- 設置車輛控制 local vehicleSeat = car:WaitForChild("VehicleSeat") -- 當玩家進入車輛時 vehicleSeat.Changed:Connect(function(property) if property == "Occupant" then local player = vehicleSeat.Occupant if player then -- 玩家進入車輛 print("玩家進入車輛") else -- 玩家離開車輛 print("玩家離開車輛") end end end) -- 主要控制循環 game:GetService("RunService").Heartbeat:Connect(function(deltaTime) if not vehicleSeat.Occupant then return end local throttle = vehicleSeat.ThrottleFloat -- -1 到 1 之間的值 local steer = vehicleSeat.SteerFloat -- -1 到 1 之間的值 -- 計算車輛當前速度 local velocity = body.Velocity local speed = velocity.Magnitude -- 應用前進/後退力量 local forceVector = body.CFrame.LookVector local force = Vector3.new(0, 0, 0) if throttle > 0 then -- 前進 force = forceVector * FORWARD_FORCE * throttle elseif throttle < 0 then -- 後退 force = forceVector * -REVERSE_FORCE * math.abs(throttle) end -- 限制最大速度 if speed > MAX_SPEED and throttle > 0 then force = Vector3.new(0, 0, 0) end -- 應用轉向 local turnAngle = steer * TURN_SPEED * deltaTime if math.abs(throttle) > 0.1 then -- 只有在移動時才轉向 body.CFrame = body.CFrame * CFrame.Angles(0, turnAngle, 0) end -- 應用力量到車身 body:ApplyForce(force) -- 旋轉車輪 local wheelRotationSpeed = speed * 0.5 if throttle > 0 then -- 前進時車輪正向旋轉 flHinge.AngularVelocity = wheelRotationSpeed frHinge.AngularVelocity = wheelRotationSpeed rlHinge.AngularVelocity = wheelRotationSpeed rrHinge.AngularVelocity = wheelRotationSpeed elseif throttle < 0 then -- 後退時車輪反向旋轉 flHinge.AngularVelocity = -wheelRotationSpeed frHinge.AngularVelocity = -wheelRotationSpeed rlHinge.AngularVelocity = -wheelRotationSpeed rrHinge.AngularVelocity = -wheelRotationSpeed else -- 停止時車輪不旋轉 flHinge.AngularVelocity = 0 frHinge.AngularVelocity = 0 rlHinge.AngularVelocity = 0 rrHinge.AngularVelocity = 0 end -- 轉向前輪 frontLeftWheel.CFrame = frontLeftWheel.CFrame * CFrame.Angles(0, steer * 0.5, 0) frontRightWheel.CFrame = frontRightWheel.CFrame * CFrame.Angles(0, steer * 0.5, 0) end) -- 設置攝影機 local function setupCamera(player) local character = player.Character or player.CharacterAdded:Wait() local humanoid = character:WaitForChild("Humanoid") -- 設置第三人稱攝影機 humanoid.CameraOffset = Vector3.new(0, 2, 0) end vehicleSeat.Changed:Connect(function(property) if property == "Occupant" and vehicleSeat.Occupant then local player = game.Players:GetPlayerFromCharacter(vehicleSeat.Occupant.Parent) if player then setupCamera(player) end end end) `; const scoringSystemScript = ` -- 計分系統腳本 -- 常數設定 local CHECKPOINTS_TO_COMPLETE = 8 -- 完成一圈所需的檢查點數量 local FINISH_MESSAGE = "恭喜完成比賽!" -- 服務引用 local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- 創建遠端事件用於更新UI local checkpointEvent = Instance.new("RemoteEvent") checkpointEvent.Name = "CheckpointEvent" checkpointEvent.Parent = ReplicatedStorage -- 玩家數據儲存 local playerData = {} -- 初始化玩家數據 local function initPlayerData(player) playerData[player.UserId] = { checkpointsPassed = 0, lastCheckpoint = 0, raceCompleted = false, startTime = 0, finishTime = 0 } end -- 當玩家加入遊戲時 Players.PlayerAdded:Connect(function(player) initPlayerData(player) -- 創建計分UI local scoreGui = Instance.new("ScreenGui") scoreGui.Name = "RaceScoreGui" local frame = Instance.new("Frame") frame.Size = UDim2.new(0, 200, 0, 100) frame.Position = UDim2.new(0.85, -100, 0, 10) frame.BackgroundTransparency = 0.5 frame.BackgroundColor3 = Color3.fromRGB(0, 0, 0) frame.Parent = scoreGui local checkpointLabel = Instance.new("TextLabel") checkpointLabel.Name = "CheckpointLabel" checkpointLabel.Size = UDim2.new(1, 0, 0.5, 0) checkpointLabel.Text = "檢查點: 0/" .. CHECKPOINTS_TO_COMPLETE checkpointLabel.TextColor3 = Color3.fromRGB(255, 255, 255) checkpointLabel.BackgroundTransparency = 1 checkpointLabel.Parent = frame local timeLabel = Instance.new("TextLabel") timeLabel.Name = "TimeLabel" timeLabel.Size = UDim2.new(1, 0, 0.5, 0) timeLabel.Position = UDim2.new(0, 0, 0.5, 0) timeLabel.Text = "時間: 0.00" timeLabel.TextColor3 = Color3.fromRGB(255, 255, 255) timeLabel.BackgroundTransparency = 1 timeLabel.Parent = frame scoreGui.Parent = player.PlayerGui -- 當玩家角色加入時 player.CharacterAdded:Connect(function(character) -- 重置玩家數據 initPlayerData(player) -- 更新UI checkpointEvent:FireClient(player, "reset", 0, CHECKPOINTS_TO_COMPLETE) end) end) -- 當玩家離開遊戲時 Players.PlayerRemoving:Connect(function(player) playerData[player.UserId] = nil end) -- 設置檢查點觸發器 local function setupCheckpoint(checkpoint, checkpointNumber) checkpoint.Touched:Connect(function(hit) local character = hit.Parent local player = Players:GetPlayerFromCharacter(character) if player and not playerData[player.UserId].raceCompleted then local data = playerData[player.UserId] -- 確保檢查點按順序通過 if checkpointNumber == (data.lastCheckpoint % CHECKPOINTS_TO_COMPLETE) + 1 then data.checkpointsPassed = data.checkpointsPassed + 1 data.lastCheckpoint = checkpointNumber -- 如果是第一個檢查點且剛開始比賽,記錄開始時間 if checkpointNumber == 1 and data.startTime == 0 then data.startTime = tick() end -- 更新UI checkpointEvent:FireClient(player, "update", data.checkpointsPassed, CHECKPOINTS_TO_COMPLETE) -- 檢查是否完成比賽 (完成一圈8字形) if data.checkpointsPassed >= CHECKPOINTS_TO_COMPLETE then data.raceCompleted = true data.finishTime = tick() - data.startTime -- 通知玩家完成比賽 checkpointEvent:FireClient(player, "complete", data.finishTime) -- 顯示完成訊息 game.StarterGui:SetCore("ChatMakeSystemMessage", { Text = FINISH_MESSAGE .. "\\n完成時間: " .. string.format("%.2f", data.finishTime) .. " 秒", Color = Color3.fromRGB(0, 255, 0), Font = Enum.Font.SourceSansBold }) end end end end) end -- 尋找並設置所有檢查點 local function setupAllCheckpoints() local checkpoints = workspace:FindFirstChild("Checkpoints") if checkpoints then for i, checkpoint in ipairs(checkpoints:GetChildren()) do if checkpoint:IsA("BasePart") and checkpoint.Name:match("Checkpoint%d+") then local number = tonumber(checkpoint.Name:match("%d+")) if number then setupCheckpoint(checkpoint, number) end end end else warn("找不到檢查點資料夾!請確保在工作區中有一個名為 'Checkpoints' 的資料夾,其中包含命名為 'Checkpoint1', 'Checkpoint2' 等的檢查點。") end end -- 設置客戶端UI更新 local function setupClientUI() local clientScript = Instance.new("LocalScript") clientScript.Name = "RaceUIUpdater" clientScript.Source = [[ local ReplicatedStorage = game:GetService("ReplicatedStorage") local checkpointEvent = ReplicatedStorage:WaitForChild("CheckpointEvent") local player = game.Players.LocalPlayer local scoreGui = player.PlayerGui:WaitForChild("RaceScoreGui") local frame = scoreGui:WaitForChild("Frame") local checkpointLabel = frame:WaitForChild("CheckpointLabel") local timeLabel = frame:WaitForChild("TimeLabel") local startTime = 0 local isRacing = false -- 更新時間顯示 game:GetService("RunService").RenderStepped:Connect(function() if isRacing and startTime > 0 then local currentTime = tick() - startTime timeLabel.Text = "時間: " .. string.format("%.2f", currentTime) end end) -- 處理檢查點事件 checkpointEvent.OnClientEvent:Connect(function(action, value, total) if action == "update" then checkpointLabel.Text = "檢查點: " .. value .. "/" .. total -- 如果是第一個檢查點,開始計時 if value == 1 and not isRacing then startTime = tick() isRacing = true end elseif action == "complete" then isRacing = false timeLabel.Text = "完成時間: " .. string.format("%.2f", value) checkpointLabel.TextColor3 = Color3.fromRGB(0, 255, 0) timeLabel.TextColor3 = Color3.fromRGB(0, 255, 0) elseif action == "reset" then isRacing = false startTime = 0 checkpointLabel.Text = "檢查點: 0/" .. total timeLabel.Text = "時間: 0.00" checkpointLabel.TextColor3 = Color3.fromRGB(255, 255, 255) timeLabel.TextColor3 = Color3.fromRGB(255, 255, 255) end end) ]] clientScript.Parent = ReplicatedStorage end -- 初始化 setupAllCheckpoints() setupClientUI() print("賽車計分系統已初始化!") `; export default function Lesson2Page() { const [activeSection, setActiveSection] = useState('intro'); const scrollToSection = (sectionId: string) => { setActiveSection(sectionId); const element = document.getElementById(sectionId); if (element) { element.scrollIntoView({ behavior: 'smooth' }); } }; return (
{/* Sidebar Navigation */}

課程目錄

{/* Main Content */}

Roblox 基礎入門第二課 - 創造我的世界:賽車遊戲學習

學習建構賽車模型、設計賽道、編寫控制腳本與計分系統

{/* Course Introduction */}

課程簡介

本課程將帶領學習者建構一個完整的賽車遊戲,包含四輪車模型、8字形賽道、車輛控制系統與計分機制。通過實際操作,學習者將掌握 Roblox Studio 中的模型建構、程式控制與遊戲機制設計等核心技能。

課程目標

  • 在 Roblox Studio 中建構可操作的四輪車模型
  • 設計並實現 8 字形賽道與檢查點系統
  • 編寫車輛控制腳本,實現前進、後退、左右轉向功能
  • 設計計分機制,追蹤賽車完成圈數並判定比賽完成

前置準備

  • 已安裝最新版本的 Roblox Studio
  • 具備 Roblox Studio 基本操作能力(第一課內容)
  • 了解基本的 Lua 程式語法
{/* Part 1: Car Model Construction */}

第一部分:賽車模型建構

步驟 1:創建新專案

  1. 打開 Roblox Studio
  2. 選擇「基礎模板」創建新專案
  3. 保存專案,命名為「RaceCarGame」

步驟 2:建立車身基本結構

  1. 在工作區中插入一個方塊部件(Part)
  2. 調整車身尺寸與位置:Size: 6, 2, 10 (寬, 高, 長), Position: 0, 3, 0 (X, Y, Z), 命名為「Body」
  3. 設置車身外觀:選擇喜歡的顏色, Material: SmoothPlastic, Anchored: false
  4. 設置物理屬性:CanCollide: true, Massless: false, CustomPhysicalProperties: true (Density: 1, Friction: 0.3, Elasticity: 0.2)

步驟 3:創建車輪

  1. 創建前左輪:插入圓柱體(Cylinder), Size: 2, 0.5, 2, Position: -3, 2, 3.5, Orientation: 0, 0, 90, 命名為「FrontLeftWheel」
  2. 創建前右輪:複製前左輪, Position: 3, 2, 3.5, 命名為「FrontRightWheel」
  3. 創建後左輪:複製前左輪, Position: -3, 2, -3.5, 命名為「RearLeftWheel」
  4. 創建後右輪:複製前左輪, Position: 3, 2, -3.5, 命名為「RearRightWheel」
  5. 設置車輪外觀:Color: 黑色, Material: Rubber, CanCollide: true

步驟 4:連接車輪與車身

  1. 創建前左輪連接點:使用 HingeConstraint, Attachment0 連接 Body, Attachment1 連接 FrontLeftWheel, 命名為「FLHinge」
  2. 設置前左輪連接點屬性:ActuatorType: Motor, AngularVelocity: 0, MotorMaxTorque: 1000
  3. 重複上述步驟,為其他三個車輪創建連接點 (FRHinge, RLHinge, RRHinge)

步驟 5:添加車身細節

  • 創建擋風玻璃:使用方塊部件, 設置大小、位置、顏色(淺藍)、透明度(0.5)、材質(Glass)
  • 創建前燈:使用兩個小方塊部件, 放置在車身前部, 設置顏色(黃/白), 可選添加 PointLight
  • 創建後燈:使用兩個小方塊部件, 放置在車身後部, 設置顏色(紅)
  • 創建座椅與方向盤:使用方塊和圓環部件

步驟 6:添加駕駛座

  1. 插入 VehicleSeat:放置在車身上方中央, 命名為「VehicleSeat」
  2. 設置 VehicleSeat 屬性:MaxSpeed: 60, Torque: 200, TurnSpeed: 2.5
  3. 將所有部件組織到一個模型中:選中所有部件, 右鍵「Group」, 命名為「RaceCar」
{/* Part 2: Figure-8 Track Design */}

第二部分:8字形賽道設計

步驟 1:規劃賽道布局

  1. 創建賽道基礎平面:插入大型方塊部件, Size: 100, 1, 100, Position: 0, 0, 0, Color: 綠色, Material: Grass, Anchored: true
  2. 規劃 8 字形賽道路徑:在基礎平面上方繪製, 確保寬度足夠(12-15 studs), 彎道半徑適當

步驟 2:建構賽道

  1. 創建賽道部件:使用多個方塊部件組合成 8 字形, 設置大小、位置、顏色(灰色)、材質(Concrete)、Anchored: true
  2. 使用 Union 工具合併賽道部件...賽道部件, 點擊「Model」{'>'}「Union」, 命名為「Track...

步驟 3:添加賽道邊界

  1. 創建護欄:沿賽道外圍放置方塊部件, 設置大小、顏色(紅白相間)、材質(SmoothPlastic)、Anchored: true
  2. 調整彎道處的護欄:使用多個小段護欄拼接, 確保護欄緊貼賽道邊緣

步驟 4:創建檢查點系統

  1. 創建檢查點資料夾:在工作區創建 Folder, 命名為「Checkpoints」
  2. 創建檢查點觸發器:在關鍵位置插入方塊部件, Size: 賽道寬度, 5, 1, Transparency: 0.8, CanCollide: false, 命名為「Checkpoint1」、「Checkpoint2」等, 按順序排列
  3. 創建起點/終點線:在賽道上創建明顯的起點/終點線, 與第一個檢查點重合
{/* Part 3: Vehicle Control System */}

第三部分:車輛控制系統

步驟 1:創建車輛控制腳本

  1. 在 RaceCar 模型中添加腳本, 命名為「CarController」
  2. 編寫車輛控制腳本(見下方代碼)
  3. 保存腳本
{carControllerScript}

步驟 2:測試車輛控制

  1. 點擊「Play」按鈕進入測試模式
  2. 使用 W/A/S/D 或方向鍵駕駛車輛
  3. 測試加速、轉向和剎車功能
  4. 點擊「Stop」按鈕退出測試模式

步驟 3:調整車輛控制參數

根據測試結果調整 CarController 腳本中的參數:

  • FORWARD_FORCE, REVERSE_FORCE, MAX_SPEED:調整速度與力量
  • TURN_SPEED:調整轉向靈敏度
  • 物理屬性:調整車身和車輪的 Density, Friction, Elasticity
{/* Part 4: Scoring System */}

第四部分:計分系統

步驟 1:創建計分系統腳本

  1. 在 ServerScriptService 中添加腳本, 命名為「ScoringSystem」
  2. 編寫計分系統腳本(見下方代碼)
  3. 保存腳本
{scoringSystemScript}

步驟 2:測試計分系統

  1. 確保所有檢查點已正確設置在 Checkpoints 資料夾中
  2. 點擊「Play」按鈕進入測試模式
  3. 駕駛車輛通過檢查點
  4. 觀察計分 UI 是否正確更新(檢查點數量和時間)
  5. 完成一圈 8 字形賽道,確認比賽完成提示是否顯示
  6. 點擊「Stop」按鈕退出測試模式
{/* Part 5: Refinement and Export */}

第五部分:完善與匯出

步驟 1:完善遊戲細節

  • 添加遊戲說明 GUI:包含控制說明和遊戲目標
  • 添加環境裝飾:樹木、建築、天空盒等
  • 添加音效:車輛引擎聲、比賽開始/完成音效

步驟 2:最終測試與調整

  • 進行全面測試:車輛控制、檢查點、計分、性能
  • 根據測試結果進行調整:優化物理、調整檢查點、修復問題

步驟 3:匯出專案

  1. 保存專案:File {'>'}Save
  2. 發布到 Roblox:File {'>'}Publish to Roblox
  3. 匯出模型(可選):選中 RaceCar 模型, File {'>'}Export Selection
{/* Summary and Challenge */}

總結

在本課程中,我們成功建構了一個完整的賽車遊戲,包含四輪車模型、8字形賽道、車輛控制系統與計分機制。通過這個項目,學習者掌握了 Roblox Studio 中的模型建構、程式控制與遊戲機制設計等核心技能。

這些技能可以應用於更複雜的遊戲開發項目,例如多人賽車遊戲、障礙賽車場、開放世界駕駛遊戲、車輛模擬器等。

進階挑戰

想要進一步提升您的賽車遊戲?嘗試以下挑戰:

  • 添加多人競賽功能
  • 實現車輛損壞系統
  • 添加不同類型的車輛
  • 創建更複雜的賽道,包含坡道、跳台等
  • 添加道具系統(如加速、護盾等)

資源推薦

{/* Navigation Buttons */}
上一課:基礎入門 下一課:互動機關
); }