Spaces:
Sleeping
Sleeping
| 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 ( | |
| <div className="flex flex-col md:flex-row gap-6"> | |
| {/* Sidebar Navigation */} | |
| <div className="md:w-1/4 lg:w-1/5"> | |
| <div className="bg-white rounded-xl shadow-md p-6 sticky top-24"> | |
| <h3 className="text-lg font-bold text-gray-800 mb-4">課程目錄</h3> | |
| <nav className="space-y-2"> | |
| <button | |
| onClick={() => scrollToSection('intro')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'intro' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 課程簡介 | |
| </button> | |
| <button | |
| onClick={() => scrollToSection('part1')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'part1' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 賽車模型建構 | |
| </button> | |
| <button | |
| onClick={() => scrollToSection('part2')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'part2' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 8字形賽道設計 | |
| </button> | |
| <button | |
| onClick={() => scrollToSection('part3')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'part3' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 車輛控制系統 | |
| </button> | |
| <button | |
| onClick={() => scrollToSection('part4')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'part4' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 計分系統 | |
| </button> | |
| <button | |
| onClick={() => scrollToSection('part5')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'part5' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 完善與匯出 | |
| </button> | |
| <button | |
| onClick={() => scrollToSection('summary')} | |
| className={`w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors ${ | |
| activeSection === 'summary' | |
| ? 'bg-green-100 text-green-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| 總結與挑戰 | |
| </button> | |
| </nav> | |
| </div> | |
| </div> | |
| {/* Main Content */} | |
| <div className="md:w-3/4 lg:w-4/5"> | |
| <motion.div | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| transition={{ duration: 0.5 }} | |
| > | |
| <div className="bg-gradient-to-r from-green-600 to-teal-600 rounded-xl p-8 mb-8 text-white"> | |
| <h1 className="text-3xl md:text-4xl font-bold mb-4">Roblox 基礎入門第二課 - 創造我的世界:賽車遊戲學習</h1> | |
| <p className="text-lg opacity-90"> | |
| 學習建構賽車模型、設計賽道、編寫控制腳本與計分系統 | |
| </p> | |
| </div> | |
| {/* Course Introduction */} | |
| <section id="intro" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">課程簡介</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6"> | |
| <p className="text-gray-700 mb-4"> | |
| 本課程將帶領學習者建構一個完整的賽車遊戲,包含四輪車模型、8字形賽道、車輛控制系統與計分機制。通過實際操作,學習者將掌握 Roblox Studio 中的模型建構、程式控制與遊戲機制設計等核心技能。 | |
| </p> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">課程目標</h3> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700"> | |
| <li>在 Roblox Studio 中建構可操作的四輪車模型</li> | |
| <li>設計並實現 8 字形賽道與檢查點系統</li> | |
| <li>編寫車輛控制腳本,實現前進、後退、左右轉向功能</li> | |
| <li>設計計分機制,追蹤賽車完成圈數並判定比賽完成</li> | |
| </ul> | |
| <h3 className="text-xl font-bold text-green-700 mt-6 mb-3">前置準備</h3> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700"> | |
| <li>已安裝最新版本的 Roblox Studio</li> | |
| <li>具備 Roblox Studio 基本操作能力(第一課內容)</li> | |
| <li>了解基本的 Lua 程式語法</li> | |
| </ul> | |
| </div> | |
| </section> | |
| {/* Part 1: Car Model Construction */} | |
| <section id="part1" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">第一部分:賽車模型建構</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 1:創建新專案</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700"> | |
| <li>打開 Roblox Studio</li> | |
| <li>選擇「基礎模板」創建新專案</li> | |
| <li>保存專案,命名為「RaceCarGame」</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 2:建立車身基本結構</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>在工作區中插入一個方塊部件(Part)</li> | |
| <li>調整車身尺寸與位置:Size: 6, 2, 10 (寬, 高, 長), Position: 0, 3, 0 (X, Y, Z), 命名為「Body」</li> | |
| <li>設置車身外觀:選擇喜歡的顏色, Material: SmoothPlastic, Anchored: false</li> | |
| <li>設置物理屬性:CanCollide: true, Massless: false, CustomPhysicalProperties: true (Density: 1, Friction: 0.3, Elasticity: 0.2)</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 3:創建車輪</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建前左輪:插入圓柱體(Cylinder), Size: 2, 0.5, 2, Position: -3, 2, 3.5, Orientation: 0, 0, 90, 命名為「FrontLeftWheel」</li> | |
| <li>創建前右輪:複製前左輪, Position: 3, 2, 3.5, 命名為「FrontRightWheel」</li> | |
| <li>創建後左輪:複製前左輪, Position: -3, 2, -3.5, 命名為「RearLeftWheel」</li> | |
| <li>創建後右輪:複製前左輪, Position: 3, 2, -3.5, 命名為「RearRightWheel」</li> | |
| <li>設置車輪外觀:Color: 黑色, Material: Rubber, CanCollide: true</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 4:連接車輪與車身</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建前左輪連接點:使用 HingeConstraint, Attachment0 連接 Body, Attachment1 連接 FrontLeftWheel, 命名為「FLHinge」</li> | |
| <li>設置前左輪連接點屬性:ActuatorType: Motor, AngularVelocity: 0, MotorMaxTorque: 1000</li> | |
| <li>重複上述步驟,為其他三個車輪創建連接點 (FRHinge, RLHinge, RRHinge)</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 5:添加車身細節</h3> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建擋風玻璃:使用方塊部件, 設置大小、位置、顏色(淺藍)、透明度(0.5)、材質(Glass)</li> | |
| <li>創建前燈:使用兩個小方塊部件, 放置在車身前部, 設置顏色(黃/白), 可選添加 PointLight</li> | |
| <li>創建後燈:使用兩個小方塊部件, 放置在車身後部, 設置顏色(紅)</li> | |
| <li>創建座椅與方向盤:使用方塊和圓環部件</li> | |
| </ul> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 6:添加駕駛座</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>插入 VehicleSeat:放置在車身上方中央, 命名為「VehicleSeat」</li> | |
| <li>設置 VehicleSeat 屬性:MaxSpeed: 60, Torque: 200, TurnSpeed: 2.5</li> | |
| <li>將所有部件組織到一個模型中:選中所有部件, 右鍵「Group」, 命名為「RaceCar」</li> | |
| </ol> | |
| </div> | |
| </section> | |
| {/* Part 2: Figure-8 Track Design */} | |
| <section id="part2" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">第二部分:8字形賽道設計</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 1:規劃賽道布局</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建賽道基礎平面:插入大型方塊部件, Size: 100, 1, 100, Position: 0, 0, 0, Color: 綠色, Material: Grass, Anchored: true</li> | |
| <li>規劃 8 字形賽道路徑:在基礎平面上方繪製, 確保寬度足夠(12-15 studs), 彎道半徑適當</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 2:建構賽道</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建賽道部件:使用多個方塊部件組合成 8 字形, 設置大小、位置、顏色(灰色)、材質(Concrete)、Anchored: true</li> | |
| <li>使用 Union 工具合併賽道部件...賽道部件, 點擊「Model」{'>'}「Union」, 命名為「Track...</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 3:添加賽道邊界</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建護欄:沿賽道外圍放置方塊部件, 設置大小、顏色(紅白相間)、材質(SmoothPlastic)、Anchored: true</li> | |
| <li>調整彎道處的護欄:使用多個小段護欄拼接, 確保護欄緊貼賽道邊緣</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 4:創建檢查點系統</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>創建檢查點資料夾:在工作區創建 Folder, 命名為「Checkpoints」</li> | |
| <li>創建檢查點觸發器:在關鍵位置插入方塊部件, Size: 賽道寬度, 5, 1, Transparency: 0.8, CanCollide: false, 命名為「Checkpoint1」、「Checkpoint2」等, 按順序排列</li> | |
| <li>創建起點/終點線:在賽道上創建明顯的起點/終點線, 與第一個檢查點重合</li> | |
| </ol> | |
| </div> | |
| </section> | |
| {/* Part 3: Vehicle Control System */} | |
| <section id="part3" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">第三部分:車輛控制系統</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 1:創建車輛控制腳本</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>在 RaceCar 模型中添加腳本, 命名為「CarController」</li> | |
| <li>編寫車輛控制腳本(見下方代碼)</li> | |
| <li>保存腳本</li> | |
| </ol> | |
| <SyntaxHighlighter language="lua" style={vscDarkPlus} className="rounded-lg"> | |
| {carControllerScript} | |
| </SyntaxHighlighter> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 2:測試車輛控制</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>點擊「Play」按鈕進入測試模式</li> | |
| <li>使用 W/A/S/D 或方向鍵駕駛車輛</li> | |
| <li>測試加速、轉向和剎車功能</li> | |
| <li>點擊「Stop」按鈕退出測試模式</li> | |
| </ol> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 3:調整車輛控制參數</h3> | |
| <p className="text-gray-700 mb-4"> | |
| 根據測試結果調整 CarController 腳本中的參數: | |
| </p> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700"> | |
| <li>FORWARD_FORCE, REVERSE_FORCE, MAX_SPEED:調整速度與力量</li> | |
| <li>TURN_SPEED:調整轉向靈敏度</li> | |
| <li>物理屬性:調整車身和車輪的 Density, Friction, Elasticity</li> | |
| </ul> | |
| </div> | |
| </section> | |
| {/* Part 4: Scoring System */} | |
| <section id="part4" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">第四部分:計分系統</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 1:創建計分系統腳本</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>在 ServerScriptService 中添加腳本, 命名為「ScoringSystem」</li> | |
| <li>編寫計分系統腳本(見下方代碼)</li> | |
| <li>保存腳本</li> | |
| </ol> | |
| <SyntaxHighlighter language="lua" style={vscDarkPlus} className="rounded-lg"> | |
| {scoringSystemScript} | |
| </SyntaxHighlighter> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 2:測試計分系統</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700 mb-4"> | |
| <li>確保所有檢查點已正確設置在 Checkpoints 資料夾中</li> | |
| <li>點擊「Play」按鈕進入測試模式</li> | |
| <li>駕駛車輛通過檢查點</li> | |
| <li>觀察計分 UI 是否正確更新(檢查點數量和時間)</li> | |
| <li>完成一圈 8 字形賽道,確認比賽完成提示是否顯示</li> | |
| <li>點擊「Stop」按鈕退出測試模式</li> | |
| </ol> | |
| </div> | |
| </section> | |
| {/* Part 5: Refinement and Export */} | |
| <section id="part5" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">第五部分:完善與匯出</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 1:完善遊戲細節</h3> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700"> | |
| <li>添加遊戲說明 GUI:包含控制說明和遊戲目標</li> | |
| <li>添加環境裝飾:樹木、建築、天空盒等</li> | |
| <li>添加音效:車輛引擎聲、比賽開始/完成音效</li> | |
| </ul> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 2:最終測試與調整</h3> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700"> | |
| <li>進行全面測試:車輛控制、檢查點、計分、性能</li> | |
| <li>根據測試結果進行調整:優化物理、調整檢查點、修復問題</li> | |
| </ul> | |
| </div> | |
| <div className="bg-white rounded-xl shadow-md p-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">步驟 3:匯出專案</h3> | |
| <ol className="list-decimal pl-6 space-y-2 text-gray-700"> | |
| <li>保存專案:File {'>'}Save</li> | |
| <li>發布到 Roblox:File {'>'}Publish to Roblox</li> | |
| <li>匯出模型(可選):選中 RaceCar 模型, File {'>'}Export Selection</li> | |
| </ol> | |
| </div> | |
| </section> | |
| {/* Summary and Challenge */} | |
| <section id="summary" className="mb-12 scroll-mt-24"> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-4">總結</h2> | |
| <div className="bg-white rounded-xl shadow-md p-6 mb-6"> | |
| <p className="text-gray-700 mb-4"> | |
| 在本課程中,我們成功建構了一個完整的賽車遊戲,包含四輪車模型、8字形賽道、車輛控制系統與計分機制。通過這個項目,學習者掌握了 Roblox Studio 中的模型建構、程式控制與遊戲機制設計等核心技能。 | |
| </p> | |
| <p className="text-gray-700 mb-4"> | |
| 這些技能可以應用於更複雜的遊戲開發項目,例如多人賽車遊戲、障礙賽車場、開放世界駕駛遊戲、車輛模擬器等。 | |
| </p> | |
| </div> | |
| <div className="bg-gradient-to-r from-green-50 to-teal-50 rounded-xl shadow-md p-6 mb-6"> | |
| <h3 className="text-xl font-bold text-green-700 mb-3">進階挑戰</h3> | |
| <p className="text-gray-700 mb-4"> | |
| 想要進一步提升您的賽車遊戲?嘗試以下挑戰: | |
| </p> | |
| <ul className="list-disc pl-6 space-y-2 text-gray-700"> | |
| <li>添加多人競賽功能</li> | |
| <li>實現車輛損壞系統</li> | |
| <li>添加不同類型的車輛</li> | |
| <li>創建更複雜的賽道,包含坡道、跳台等</li> | |
| <li>添加道具系統(如加速、護盾等)</li> | |
| </ul> | |
| </div> | |
| <div className="bg-gradient-to-r from-blue-50 to-green-50 rounded-xl shadow-md p-6"> | |
| <h3 className="text-xl font-bold text-blue-700 mb-3">資源推薦</h3> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <a href="https://create.roblox.com/docs" target="_blank" rel="noopener noreferrer" className="bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow"> | |
| <h4 className="font-bold text-blue-800 mb-2">Roblox 開發者文檔</h4> | |
| <p className="text-gray-700">官方開發文檔,包含詳細的 API 參考和教程</p> | |
| </a> | |
| <a href="https://developer.roblox.com/en-us/articles/vehicles" target="_blank" rel="noopener noreferrer" className="bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow"> | |
| <h4 className="font-bold text-blue-800 mb-2">Roblox 車輛系統教程</h4> | |
| <p className="text-gray-700">官方關於車輛系統的詳細教程</p> | |
| </a> | |
| <a href="https://www.lua.org/manual/5.1/" target="_blank" rel="noopener noreferrer" className="bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow"> | |
| <h4 className="font-bold text-blue-800 mb-2">Lua 程式設計指南</h4> | |
| <p className="text-gray-700">學習 Lua 語言的官方手冊</p> | |
| </a> | |
| <a href="https://devforum.roblox.com/" target="_blank" rel="noopener noreferrer" className="bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow"> | |
| <h4 className="font-bold text-blue-800 mb-2">Roblox 開發者論壇</h4> | |
| <p className="text-gray-700">與其他開發者交流,獲取幫助和分享經驗</p> | |
| </a> | |
| </div> | |
| </div> | |
| </section> | |
| {/* Navigation Buttons */} | |
| <div className="flex justify-between mt-12"> | |
| <a | |
| href="/lesson1" | |
| className="px-6 py-3 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition-colors flex items-center" | |
| > | |
| <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fillRule="evenodd" d="M9.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L7.414 9H15a1 1 0 110 2H7.414l2.293 2.293a1 1 0 010 1.414z" clipRule="evenodd" /> | |
| </svg> | |
| 上一課:基礎入門 | |
| </a> | |
| <a | |
| href="/lesson3" | |
| className="px-6 py-3 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors flex items-center" | |
| > | |
| 下一課:互動機關 | |
| <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 ml-2" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fillRule="evenodd" d="M10.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L12.586 11H5a1 1 0 110-2h7.586l-2.293-2.293a1 1 0 010-1.414z" clipRule="evenodd" /> | |
| </svg> | |
| </a> | |
| </div> | |
| </motion.div> | |
| </div> | |
| </div> | |
| ); | |
| } | |