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 (
學習建構賽車模型、設計賽道、編寫控制腳本與計分系統
本課程將帶領學習者建構一個完整的賽車遊戲,包含四輪車模型、8字形賽道、車輛控制系統與計分機制。通過實際操作,學習者將掌握 Roblox Studio 中的模型建構、程式控制與遊戲機制設計等核心技能。
根據測試結果調整 CarController 腳本中的參數:
在本課程中,我們成功建構了一個完整的賽車遊戲,包含四輪車模型、8字形賽道、車輛控制系統與計分機制。通過這個項目,學習者掌握了 Roblox Studio 中的模型建構、程式控制與遊戲機制設計等核心技能。
這些技能可以應用於更複雜的遊戲開發項目,例如多人賽車遊戲、障礙賽車場、開放世界駕駛遊戲、車輛模擬器等。
想要進一步提升您的賽車遊戲?嘗試以下挑戰: