RobloxでTycoonを作成するために、Zednov's Tycoon Kit を理解してみた
はじめに(記事の概要)
この記事では、Robloxで人気の"Zednov's Tycoon Kit"について解説します。このKitの使い方から中身の構成まで、Roblox初心者でも簡単にTycoonゲームを作れるようになる情報を提供できるよう頑張ります。
コンテキスト
- Roblox Studioの基本的な使い方はわかる
- プログラミングの基礎知識がある(Lua言語が理解できるとなお良い)
対象読者
- Robloxの初心者
- プログラミングはある程度理解している
- RobloxでTycoonゲームを作ってみたいと思っている
- どのスクリプトをWorkspaceやServerScriptServiceなどどこに配置するのか知りたい
セクション1: Zednov's Tycoon Kitとは?
Kitの概要
Zednov's Tycoon Kitは、RobloxでTycoonゲームを簡単に作成できるように設計されたフリーキットです。このキットを使用することで、プレイヤーは自分自身のTycoonゲームを手軽に作成できます。Lua言語を用いたスクリプトが組み込まれており、初心者からでも利用できます。
下記を使用しています。
主な機能と特長
- プレイヤー管理: 新規プレイヤーのゲーム参加及び退出時の処理は自動化されています。
- 自動チーム作成: Tycoonsフォルダ内の各Tycoon(Fighting Bearsモデル)に基づき、自動的にチームが生成されます。
- ステータス管理: プレイヤーの資源や貨幣等、ゲーム内ステータスは容易に管理可能です。
- 所有権の自動設定: プレイヤーがTycoonに接触した際、そのTycoonの所有者として自動設定されます。
- Dev Productボタン: プレイヤーに特定の開発製品を購入させる機能が存在します。
- Gamepassボタン: 特定のGamepass(有料アイテム)を所有するプレイヤーのみが利用可能な機能を設定可能です。
- サウンド機能: ゲーム体験を高めるためのサウンド設定が用意されています。
このように、Zednov's Tycoon Kitは多くの便利な機能と特長を備えています。これにより、ゲーム作成がよりスムーズに、そして楽しくなります。
セクション2: Kitのインストール方法
Roblox Studioでのインストール手順
- Roblox Studioを開く: まずはRoblox Studioを開いて、新しいプロジェクトを作成または既存のプロジェクトを開きます。
-
ツールバーからViewを選択: 上部のツールバーから
View
をクリックします。 -
Toolboxを開く:
View
タブの中からToolbox
を選択して開きます。 -
Zednov's Tycoon Kitを検索:
Toolbox
の検索バーにZednov's Tycoon Kit
と入力して検索します。 - Kitをプロジェクトに追加: 検索結果からZednov's Tycoon Kitを見つけたら、それをクリックしてプロジェクトに追加します。
セクション3: Kitの中身の解説
Zednov's Tycoon Kitにはいくつかの主要なコンポーネントが含まれています。それぞれの役割と機能について詳しく見ていきましょう。
GameEssentials
このフォルダは、ゲームに必要な基本的な要素が含まれています。通常、プレイヤーのスポーン地点やゲーム内の基本的なオブジェクトがここに配置されます。
デフォルトでは何も入っておらず、空の状態です。
自分で作成するゲームの要素を格納するのだと思われます。
Tycoons
このフォルダには、各Tycoonのデータと設定が含まれています。プレイヤーが所有できる各Tycoonの設定やオブジェクトがここに保存されています。
初期ではFighting Bearsモデルが配置されており、中身は下記のようになっています。
Fighting Bears
|-BuyObject :空。使われていなさそう
|-CurrencyToCollect :(IntValueオブジェクト)取得前の稼いだ金額をプールしておく場所
|-DropColor :DropItemの色設定
|-MaterialValue :素材の質感(デフォルトはPlastic)
|-Owner :所有者(プレイヤー)のユーザー名格納用オブジェクト
|-PurchaseHandler :購入関連処理のスクリプト(詳細後術)
|-TeamColor :チームの色(各タイクーンごとにチーム化している)
|-Buttons :購入用ボタンのモデルが格納されている
|-DevProductButton Example :DevProductの購入サンプル
|-Entrance :入り口のゲート(通ると自分のTycoonとなる
|-Essentials :初期配置される要素
|-Gamepass Button:ゲームパス(有料アイテム)用ボタンのサンプル
|-PurchasedObjects :ゲーム内で購入したオブジェクトが格納されていく
|-Purchases :購入可能なオブジェクトが配置されている。PurchaseHnadlerスクリプトにて初期化時にメモリへコピーし、オブジェクトを削除している。
Core_handler (script)
配置箇所:Workspace > Zednov's Tycoon Kit > Core_Handler
CoreHandlerの要点
- 変数と設定: ゲームの基本設定と変数を初期化。
- チーム管理: 既存のチームカラーをチェックし、新しいチームを作成。
- タイクーン管理: プレイヤーが所有するタイクーンを特定。
- プレイヤーイベント: プレイヤーがゲームに参加・退出したときの処理。
DevProductHandler (script)
配置箇所:ServerScriptService > DevProductHandler
-- スクリプト内でServerScriptServiceへ配置している
script.Parent = game.ServerScriptService
DevProductHandlerの要点
-
初期設定: ゲームの設定とマーケットプレイスサービスを読み込む。
https://create.roblox.com/docs/reference/engine/classes/MarketplaceService -
DevProductsの作成: タイクーン内のボタンからDevProduct情報を取得して、DevProductsテーブルに保存。
-
レシート処理: MarketplaceServiceを使って、プレイヤーがDevProductを購入した際の処理を行う。
-
プレイヤー認証: 購入したプレイヤーのIDを確認し、対応するDevProductを特定。
-
オブジェクト生成: 購入に成功した場合、新しいオブジェクト(モデル)を生成して、プレイヤーのタイクーンに追加。
LinkedLeaderBoard (script)
配置箇所:ServerScriptService > LinkedLeaderboard
-- スクリプト内でServerScriptServiceへ配置している
script.Parent = game.ServerScriptService
LinkedLeaderBoardの要点
-
設定の読み込み:
Settings
モジュールを読み込む。 - 死亡イベント: プレイヤーが死んだときに、リーダーボードの「死亡数」を更新。
- キルカウント: プレイヤーが他のプレイヤーを倒したときに、リーダーボードの「キル数」を更新。
-
プレイヤーのリスポーン: プレイヤーがリスポーンしたときに新しい
Humanoid
にイベントを接続。 - フラグスタンドの探索: ゲーム内のすべてのフラグスタンドを探して、リスナーを設定。
- キャプチャスコア: プレイヤーがフラグをキャプチャしたときに、リーダーボードの「キャプチャ数」を更新。
- プレイヤーの入場: 新しいプレイヤーがゲームに参加したときに、リーダーボードの各種スタッツを初期化。
- CTFモード: フラグスタンドが存在する場合、CTF(Capture The Flag)モードを有効にする。
Settings (module script table data)
このモジュールスクリプトは、ゲームの設定データを格納しています。ゲームの難易度や通貨の設定など、様々なゲーム内設定がここで管理されます。
READ ME (module script read me)
このモジュールスクリプトは、Kitの使用方法や設定方法についての説明が書かれています。初めてKitを使用する人にとって、非常に有用な情報が含まれています。
以上がZednov's Tycoon Kitの主要なコンポーネントとその機能です。これらを理解することで、より効率的にTycoonゲームを開発することができます。
セクション4: Scriptの内容
主要なスクリプト
Zednov's Tycoon Kit直下
- Core_handler
- DevProductHandler
- LinkedLeaderBoard
Fighting Bears配下(各Tycoonのスクリプト)
- PurchaseHnadler
- GateControl
スクリプトの詳細
Core_handler (script)
概要
Core_handlerスクリプトは、Tycoonゲームの中心的な操作を制御します。プレイヤーの追加、Tycoonの生成、ステータスの管理など、多くの重要な処理がこのスクリプトで行われます。
コードセクションと解説
変数と設定: ゲームの基本設定と変数を初期化
-- Tycoonsという名前の空のテーブルを作成します。
-- このテーブルは後でTycoonオブジェクトを格納するために使用されます。
local Tycoons = {}
-- RobloxのTeamsサービスを取得して、Teamsという変数に格納します。
-- これで、後でチーム関連の処理が簡単に行えます。
local Teams = game:GetService('Teams')
-- 親スクリプトのSettingsモジュールをrequire関数で取得して、Settingsという変数に格納します。
-- これにより、設定情報に簡単にアクセスできます。
local Settings = require(script.Parent.Settings)
-- BrickColorクラスを短縮形としてBCという変数に格納します。
-- これで、後でBrickColor関連の処理が簡単に行えます。
local BC = BrickColor
-- 新しい'Folder'インスタンスを作成し、game.ServerStorageに格納します。
-- このフォルダは、プレイヤーのお金情報を保存するために使用されます。
local Storage = Instance.new('Folder', game.ServerStorage)
チーム管理: 既存のチームカラーをチェックし、新しいチームを作成
function returnColorTaken(color)
-- Teamsサービスの子要素をループしてチェックします。
for i,v in pairs(Teams:GetChildren()) do
-- もし子要素が'Team'タイプなら、以下の処理を行います。
if v:IsA('Team') then
-- 与えられたカラーが既に存在するチームのカラーと一致するかどうかを確認します。
if v.TeamColor == color then
-- もし一致するカラーがあれば、trueを返します。
return true
end
end
end
-- ループが終わっても一致するカラーがなければ、falseを返します。
return false
end
この関数は、指定されたチームカラーが既に使用されているかどうかを確認します。
タイクーン管理: プレイヤーが所有するタイクーンを特定
for i,v in pairs(script.Parent:WaitForChild('Tycoons'):GetChildren()) do
-- TycoonsテーブルにTycoonの名前とクローンを保存します。
-- これは後でチームを作成する際に、Tycoonの名前をチーム名として使用するためです。
Tycoons[v.Name] = v:Clone()
-- 既に使われているチームカラーかどうかを確認します。
-- Robloxでは同じチームカラーを持つチームは作成できないため、このチェックが必要です。
if returnColorTaken(v.TeamColor) then
-- 重複するチームカラーを処理します。
local newColor;
repeat
wait()
-- ランダムな新しいカラーを生成します。
-- これは重複を避けるための処理です。
newColor = BC.Random()
until returnColorTaken(newColor) == false
-- 新しいカラーをTycoonのTeamColorに設定します。
v.TeamColor.Value = newColor
end
-- 重複がないことを確認した後で、新しいチームを作成します。
-- これはプレイヤーがどのTycoonに所属しているのかを識別するためです。
local NewTeam = Instance.new('Team',Teams)
NewTeam.Name = v.Name
NewTeam.TeamColor = v.TeamColor.Value
-- 自動チーム割り当てが無効な場合、AutoAssignableをfalseにします。
-- これはプレイヤーが自分でチームを選べるようにするための設定です。
if not Settings['AutoAssignTeams'] then
NewTeam.AutoAssignable = false
end
-- PurchaseHandlerスクリプトを有効にします。
-- これはプレイヤーがアイテムを購入できるようにするための処理です。
v.PurchaseHandler.Disabled = false
end
-- getPlrTycoon関数: 引数として与えられたプレイヤーが所有するTycoonを返す関数
function getPlrTycoon(player)
-- script.Parent.Tycoonsの子要素を一つずつ調べる
for i,v in pairs(script.Parent.Tycoons:GetChildren()) do
-- 子要素が"Model"タイプであるか確認
if v:IsA("Model") then
-- ModelのOwnerプロパティが引数で与えられたプレイヤーと一致するか確認
if v.Owner.Value == player then
-- 一致した場合、そのTycoon(Model)を返す
return v
end
end
end
-- 該当するTycoonが見つからなかった場合はnilを返す
return nil
end
プレイヤーイベント: プレイヤーがゲームに参加・退出したときの処理
新規プレイヤーがゲームに参加したときや退出したときの処理がここで行われます。
-- プレイヤーがゲームに参加したときに発火するイベント
game.Players.PlayerAdded:connect(function(player)
-- ServerStorageのPlayerMoneyにNumberValueを新規作成してプレイヤーのステータスとして保存
local plrStats = Instance.new("NumberValue", game.ServerStorage.PlayerMoney)
-- そのNumberValueの名前をプレイヤーの名前に設定
plrStats.Name = player.Name
-- "OwnsTycoon"という名前のObjectValueを作成して、プレイヤーが所有するタイクーン情報を保存するための場所を作成
local isOwner = Instance.new("ObjectValue", plrStats)
isOwner.Name = "OwnsTycoon"
end)
-- プレイヤーがゲームから退出したときに発火するイベント
game.Players.PlayerRemoving:connect(function(player)
-- ServerStorageのPlayerMoneyからプレイヤーのステータスを探す
local plrStats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name)
-- もしステータスが存在すれば、それを破棄
if plrStats ~= nil then
plrStats:Destroy()
end
-- プレイヤーが所有しているタイクーンを探す(getPlrTycoonはこのコードには含まれていないが、おそらく他の部分で定義されている)
local tycoon = getPlrTycoon(player)
-- タイクーンが存在すれば
if tycoon then
-- タイクーンのバックアップを作成
local backup = Tycoons[tycoon.Name]:Clone()
-- オリジナルのタイクーンを破棄
tycoon:Destroy()
-- 少し待つ(このwaitはおそらく破棄処理が完了するのを待っている)
wait()
-- バックアップをTycoonsに再配置
backup.Parent = script.Parent.Tycoons
end
end)
DevProductHandler (script)
このスクリプトは、開発者がゲーム内で販売する商品を管理するためのものです。商品の価格設定や購入処理がここで行われます。
-- 設定モジュールを読み込む
local Settings = require(script.Parent.Settings)
-- Tycoonモデルを取得
local Tycoon = script.Parent.Tycoons:GetChildren()[1]
-- スクリプトの親をServerScriptServiceに設定
script.Parent = game.ServerScriptService
-- DevProductsを格納するテーブルを初期化
local DevProducts = {}
-- MarketplaceServiceを取得
local MarketplaceService = game:GetService('MarketplaceService')
-- Tycoon内のButtonsフォルダの各子要素(ボタン)をループで処理
for i,v in pairs(Tycoon:WaitForChild('Buttons'):GetChildren()) do
-- DevProductという名前の子要素が存在するかチェック
if v:FindFirstChild('DevProduct') then
-- DevProductのValueが0より大きいかチェック
if v.DevProduct.Value > 0 then
-- DevProductのIDをキーとして、対応するボタンをテーブルに保存
DevProducts[v.DevProduct.Value] = v
end
end
end
-- レシート処理関数を定義
MarketplaceService.ProcessReceipt = function(receiptInfo)
-- 全プレイヤーをループで処理
for i,plr in pairs(game.Players:GetPlayers()) do
-- レシートのPlayerIdがプレイヤーのuserIdと一致するかチェック
if plr.userId == receiptInfo.PlayerId then
-- 購入されたDevProductがテーブルに存在するかチェック
if DevProducts[receiptInfo.ProductId] then
-- 対応するボタンを取得
local obj = DevProducts[receiptInfo.ProductId]
-- プレイヤーが所有するTycoonを取得
local PlrT = game.ServerStorage.PlayerMoney:WaitForChild(plr.Name).OwnsTycoon
-- Tycoonが存在するかチェック
if PlrT.Value ~= nil then
-- 購入処理(Create関数)を呼び出す
local PlayerStats = game.ServerStorage.PlayerMoney:FindFirstChild(plr.Name)
Create({[1] = 0,[2] = obj,[3] = PlayerStats}, PlrT.Value.BuyObject)
end
end
end
end
end
-- 購入処理関数(Create)を定義
function Create(tab, prnt)
local x = Instance.new('Model')
Instance.new('NumberValue',x).Value = tab[1]
x.Value.Name = "Cost"
Instance.new('ObjectValue',x).Value = tab[2]
x.Value.Name = "Button"
local Obj = Instance.new('ObjectValue',x)
Obj.Name = "Stats"
Obj.Value = tab[3]
x.Parent = prnt
end
LinkedLeaderBoard (script)
このスクリプトは、ゲーム内のリーダーボードを管理しています。プレイヤーのスコアやランキング、その他のステータスを表示するためのものです。
設定の読み込み: Settings モジュールを読み込む
-- 設定モジュールを読み込む
local Settings = require(script.Parent.Settings)
-- スクリプトの親をServerScriptServiceに設定
script.Parent = game.ServerScriptService
-- FlagStandを格納するテーブルを初期化
stands = {}
-- CTFモードのフラグを初期化
CTF_mode = false
死亡イベント: プレイヤーが死んだときに、リーダーボードの「死亡数」を更新
-- プレイヤーが死んだときの処理
function onHumanoidDied(humanoid, player)
-- プレイヤーのleaderstatsを探す
local stats = player:findFirstChild("leaderstats")
if stats ~= nil then
-- 死亡数をインクリメント
local deaths = stats:findFirstChild(Settings.LeaderboardSettings.DeathsName)
if deaths then
deaths.Value = deaths.Value + 1
end
-- KOsが有効な場合、キラーを探して処理
if Settings.LeaderboardSettings.KOs then
local killer = getKillerOfHumanoidIfStillInGame(humanoid)
handleKillCount(humanoid, player)
end
end
end
プレイヤーのリスポーン: プレイヤーがリスポーンしたときに新しいHumanoidにイベントを接続
-- プレイヤーがリスポーンしたときに呼び出される関数
function onPlayerRespawn(property, player)
-- 新しいHumanoidに接続する必要がある
-- propertyが"Character"で、かつ、player.Characterがnilでない場合(つまり、プレイヤーが新しいキャラクターを持っている場合)
if property == "Character" and player.Character ~= nil then
-- プレイヤーのHumanoidを取得
local humanoid = player.Character.Humanoid
-- プレイヤーとHumanoidを変数に格納(おそらく後で使いやすくするため)
local p = player
local h = humanoid
-- 設定でWOs(おそらく死亡数)が有効な場合
if Settings.LeaderboardSettings.WOs then
-- Humanoidが死んだときにonHumanoidDied関数を呼び出す
humanoid.Died:connect(function() onHumanoidDied(h, p) end )
end
end
end
function getKillerOfHumanoidIfStillInGame(humanoid)
-- このHumanoidを倒したプレイヤーオブジェクトを返す
-- もしキラーがゲームにいない場合はnilを返す
-- Humanoidに"creator"という名前のタグがついているか確認
local tag = humanoid:findFirstChild("creator")
-- タグが存在する場合
if tag ~= nil then
-- タグのValue(キラー)を取得
local killer = tag.Value
-- キラーがまだゲームにいる場合
if killer.Parent ~= nil then
return killer
end
end
return nil
end
キルカウント: プレイヤーが他のプレイヤーを倒したときに、リーダーボードの「キル数」を更新。
-- キルカウントを処理する
function handleKillCount(humanoid, player)
-- キラーを取得
local killer = getKillerOfHumanoidIfStillInGame(humanoid)
-- キラーが存在する場合
if killer ~= nil then
-- キラーのスタッツを取得
local stats = killer:findFirstChild("leaderstats")
-- スタッツが存在する場合
if stats ~= nil then
-- キル数を取得
local kills = stats:findFirstChild(Settings.LeaderboardSettings.KillsNames)
-- キル数が存在する場合
if kills then
-- キラーとプレイヤーが同一でない場合、キル数を+1
if killer ~= player then
kills.Value = kills.Value + 1
else
-- キラーとプレイヤーが同一の場合、キル数を-1(自殺とみなす)
kills.Value = kills.Value - 1
end
else
return
end
end
end
end
フラグスタンドの探索: ゲーム内のすべてのフラグスタンドを探して、リスナーを設定
function findAllFlagStands(root)
-- rootの子要素を取得
local c = root:children()
-- 子要素をループで処理
for i=1,#c do
-- 子要素が"Model"または"Part"の場合、再帰的に探索
if (c[i].className == "Model" or c[i].className == "Part") then
findAllFlagStands(c[i])
end
-- 子要素が"FlagStand"の場合、standsテーブルに追加
if (c[i].className == "FlagStand") then
table.insert(stands, c[i])
end
end
end
function hookUpListeners()
-- standsテーブルに格納された各FlagStandに対してリスナーを設定
for i=1,#stands do
stands[i].FlagCaptured:connect(onCaptureScored)
end
end
プレイヤーの入場: 新しいプレイヤーがゲームに参加したときに、リーダーボードの各種スタッツを初期化
function onPlayerEntered(newPlayer)
-- CTFモード(Capture The Flag)が有効かどうかを確認
if CTF_mode == true then
-- leaderstatsという名前のIntValueを作成
local stats = Instance.new("IntValue")
stats.Name = "leaderstats"
-- Capturesという名前のIntValueを作成して、初期値を0に設定
local captures = Instance.new("IntValue")
captures.Name = "Captures"
captures.Value = 0
-- Capturesをleaderstatsの子要素として設定
captures.Parent = stats
-- プレイヤーのキャラクターがロードされるまで待つ(注意: この方法は最適ではない)
while true do
if newPlayer.Character ~= nil then break end
wait(5)
end
-- leaderstatsをプレイヤーの子要素として設定
stats.Parent = newPlayer
else
-- 以下、CTFモードが無効の場合の処理(KOs, WOs, 通貨など)
-- (省略している部分もあるが、基本的にはleaderstatsとその子要素を設定している)
-- プレイヤーのキャラクターがロードされるまで待つ
while true do
if newPlayer.Character ~= nil then break end
wait(5)
end
-- Humanoidが死んだときのイベントを設定
local humanoid = newPlayer.Character.Humanoid
humanoid.Died:connect(function() onHumanoidDied(humanoid, newPlayer) end )
-- 新しいHumanoidが生成されたときのイベントを設定
newPlayer.Changed:connect(function(property) onPlayerRespawn(property, newPlayer) end )
-- leaderstatsをプレイヤーの子要素として設定
stats.Parent = newPlayer
end
end
キャプチャスコア: プレイヤーがフラグをキャプチャしたときに、リーダーボードの「キャプチャ数」を更新
function onCaptureScored(player)
-- プレイヤーの子要素から"leaderstats"を探す
local ls = player:findFirstChild("leaderstats")
-- "leaderstats"がなければ、関数を終了
if ls == nil then return end
-- "leaderstats"の子要素から"Captures"を探す
local caps = ls:findFirstChild("Captures")
-- "Captures"がなければ、関数を終了
if caps == nil then return end
-- "Captures"の値を1増加させる
caps.Value = caps.Value + 1
end
-- Workspace内のすべてのフラグスタンドを探して、それらをstands配列に追加する。
findAllFlagStands(game.Workspace)
-- stands配列に含まれる各フラグスタンドに、onCaptureScored関数を接続する。
hookUpListeners()
-- フラグスタンドが1つ以上存在する場合、CTFモード(Capture The Flagモード)を有効にする。
if (#stands > 0) then CTF_mode = true end
-- 新しいプレイヤーがゲームに参加したときに、onPlayerEntered関数を呼び出す。
game.Players.ChildAdded:connect(onPlayerEntered)
PurchaseHandler
概要
- 設定のインポート: スクリプトの最初で、"Settings" モジュールスクリプトから設定をインポートしています。
- チームカラーとサウンド: チームカラーを設定し、特定のイベントでサウンドを再生する関数があります。
- パーツコレクター: ゲーム内のオブジェクト(パーツ)がコレクターに触れたときに、プレイヤーのお金(Currency)を増やします。
- プレイヤーとコレクターのインタラクション: プレイヤーがコレクターに触れたときの処理があります。所有者かどうか、健康状態などに基づいて処理が行われます。
- ボタンと購入: ゲーム内のボタンに触れたときの購入処理があります。価格、所有者、健康状態などに基づいて購入が行われます。
- 購入関数: 購入処理を行う関数があります。コストを減らし、購入したオブジェクトをプレイヤーに与えます。
- オブジェクトの作成: 新しいオブジェクト(モデル)を作成する関数があります。
- 非効率なリスナー: スクリプトの最後には、非効率なChildAddedリスナーがあり、これは改善の余地があるとコメントされています。
コードセクションと解説
設定のインポート
-- 基本設定は "Settings" モジュールスクリプトにあります。
-- 変数の初期化
local Objects = {} -- オブジェクトを格納するテーブル
local TeamColor = script.Parent.TeamColor.Value -- チームの色
local Settings = require(script.Parent.Parent.Parent.Settings) -- 設定モジュールの読み込み
local Money = script.Parent.CurrencyToCollect -- 収集する通貨
local Debris = game:GetService('Debris') -- Debrisサービスの取得
local Stealing = Settings.StealSettings -- 盗む設定
local CanSteal = true -- 盗むことができるかどうか(trueならできる)
チームカラーとサウンド
-- スポーン地点のチームカラーとブロックカラーを設定
script.Parent.Essentials.Spawn.TeamColor = TeamColor -- スポーン地点のチームカラーを設定
script.Parent.Essentials.Spawn.BrickColor = TeamColor -- スポーン地点のブロックカラーを設定
-- Sound関数: 指定されたパーツにサウンドを追加して再生する関数
function Sound(part, id)
if part:FindFirstChild('Sound') then -- もしパーツにすでに'Sound'があれば何もしない
return
else -- そうでなければ新しい'Sound'を作成
local Sound = Instance.new('Sound', part) -- 'Sound'インスタンスを作成
Sound.SoundId = "rbxassetid://" .. tostring(id) -- サウンドIDを設定
Sound:Play() -- サウンドを再生
delay(Sound.TimeLength, function() -- サウンドの長さが終わったら
Sound:Destroy() -- 'Sound'を破棄
end)
end
end
パーツコレクター
-- パーツがコレクターに落ちたときの処理
for i, v in pairs(script.Parent.Essentials:GetChildren()) do -- Essentialsの子要素をループ
if v.Name == "PartCollector" then -- 名前が"PartCollector"のものだけ処理
v.Touched:connect(function(Part) -- パーツが触れたときのイベント
if Part:FindFirstChild('Cash') then -- パーツに'Cash'があれば
Money.Value = Money.Value + Part.Cash.Value -- お金を加算
Debris:AddItem(Part, 0.1) -- パーツをDebrisに追加して0.1秒後に消去
end
end)
end
end
プレイヤーとコレクターのインタラクション
-- Player Touched Collector processor
deb = false -- デバウンス変数(連続実行を防ぐ)
script.Parent.Essentials.Giver.Touched:connect(function(hit) -- Giverに何かが触れたときのイベント
local player = game.Players:GetPlayerFromCharacter(hit.Parent) -- 触れたオブジェクトの親からプレイヤーを取得
if player ~= nil then -- プレイヤーが存在する場合
if script.Parent.Owner.Value == player then -- オーナーが触れた場合
if hit.Parent:FindFirstChild("Humanoid") then -- Humanoidが存在する場合
if hit.Parent.Humanoid.Health > 0 then -- プレイヤーが生きている場合
if deb == false then -- デバウンスがfalseの場合
deb = true -- デバウンスをtrueに設定
script.Parent.Essentials.Giver.BrickColor = BrickColor.new("Bright red") -- Giverの色を赤に変更
local Stats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name) -- プレイヤーのお金の情報を取得
if Stats ~= nil then -- お金の情報が存在する場合
Sound(script.Parent.Essentials, Settings.Sounds.Collect) -- コレクト音を再生
Stats.Value = Stats.Value + Money.Value -- お金を加算
Money.Value = 0 -- Moneyをリセット
wait(1) -- 1秒待つ
script.Parent.Essentials.Giver.BrickColor = BrickColor.new("Sea green") -- Giverの色を緑に戻す
deb = false -- デバウンスをfalseに戻す
end
end
end
end
elseif Stealing.Stealing then -- オーナーでなく、Stealingが有効な場合
if CanSteal == true then -- 盗むことができる場合
CanSteal = false -- CanStealをfalseに設定
delay(Stealing.PlayerProtection, function() -- PlayerProtection時間後に
CanSteal = true -- CanStealをtrueに戻す
end)
if hit.Parent:FindFirstChild("Humanoid") then -- Humanoidが存在する場合
if hit.Parent.Humanoid.Health > 0 then -- プレイヤーが生きている場合
local Stats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name) -- プレイヤーのお金の情報を取得
if Stats ~= nil then -- お金の情報が存在する場合
local Difference = math.floor(Money.Value * Stealing.StealPrecent) -- 盗むお金を計算
Sound(script.Parent.Essentials, Settings.Sounds.Collect) -- コレクト音を再生
Stats.Value = Stats.Value + Difference -- お金を加算
Money.Value = Money.Value - Difference -- Moneyから減算
end
end
end
else
Sound(script.Parent.Essentials, Settings.Sounds.Error) -- エラー音を再生
end
end
end
end)
ボタンと購入
このコードは、ゲーム内でプレイヤーが特定のボタン("Head"と呼ばれる部分)に触れたときに発生するイベントを処理しています。
依存オブジェクト("Dependency")が存在する場合、そのオブジェクトが存在するまでボタンは非表示となります。
プレイヤーがボタンに触れた場合、そのプレイヤーがオーナーであれば、購入処理が行われます。
script.Parent:WaitForChild("Buttons") -- "Buttons"が存在するまで待つ
for i,v in pairs(script.Parent.Buttons:GetChildren()) do -- "Buttons"の子要素をループで処理
spawn(function() -- 新しいスレッドで処理を開始
if v:FindFirstChild("Head") then -- "Head"が存在する場合
local ThingMade = script.Parent.Purchases:WaitForChild(v.Object.Value) -- 対応するオブジェクトを取得
if ThingMade ~= nil then -- オブジェクトが存在する場合
Objects[ThingMade.Name] = ThingMade:Clone() -- オブジェクトをクローンして保存
ThingMade:Destroy() -- 元のオブジェクトを削除
else -- オブジェクトが存在しない場合
-- ボタンを無効にしてエラーメッセージを出力
v.Head.CanCollide = false
v.Head.Transparency = 1
error('Object missing for button: '..v.Name..', button has been removed')
end
if v:FindFirstChild("Dependency") then -- 依存オブジェクトが存在する場合
v.Head.CanCollide = false
v.Head.Transparency = 1
coroutine.resume(coroutine.create(function() -- コルーチンで非同期処理
if script.Parent.PurchasedObjects:WaitForChild(v.Dependency.Value) then -- 依存オブジェクトが存在する場合
if Settings['ButtonsFadeIn'] then -- フェードインが有効な場合
for i=1,20 do
wait(Settings['FadeInTime']/20)
v.Head.Transparency = v.Head.Transparency - 0.05
end
end
v.Head.CanCollide = true
v.Head.Transparency = 0
end
end))
end
-- プレイヤーがボタン(Head)に触れた場合の処理を接続
v.Head.Touched:connect(function(hit)
-- 触れたオブジェクトの親からプレイヤーを取得
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
-- ボタンが衝突可能(CanCollide)である場合
if v.Head.CanCollide == true then
-- プレイヤーが存在する場合
if player ~= nil then
-- プレイヤーがこのオブジェクト(script.Parent)の所有者である場合
if script.Parent.Owner.Value == player then
-- 触れたオブジェクトがHumanoid(キャラクター)である場合
if hit.Parent:FindFirstChild("Humanoid") then
-- キャラクターの体力が0より大きい場合
if hit.Parent.Humanoid.Health > 0 then
-- プレイヤーのお金(PlayerMoney)を取得
local PlayerStats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name)
-- プレイヤーのお金が存在する場合
if PlayerStats ~= nil then
-- ゲームパスが設定されている場合
if (v:FindFirstChild('Gamepass')) and (v.Gamepass.Value >= 1) then
-- プレイヤーがゲームパスを所有している場合
if game:GetService("MarketplaceService"):UserOwnsGamePassAsync(player.userId, v.Gamepass.Value) then
Purchase({[1] = v.Price.Value,[2] = v,[3] = PlayerStats})
else
-- ゲームパス購入画面を表示
game:GetService("MarketplaceService"):PromptGamePassPurchase(player, v.Gamepass.Value)
end
-- 開発者製品が設定されている場合
elseif (v:FindFirstChild('DevProduct')) and (v.DevProduct.Value >= 1) then
-- 開発者製品購入画面を表示
game:GetService('MarketplaceService'):PromptProductPurchase(player,v.DevProduct.Value)
-- プレイヤーがアイテムを購入できる場合
elseif PlayerStats.Value >= v.Price.Value then
Purchase({[1] = v.Price.Value,[2] = v,[3] = PlayerStats})
Sound(v, Settings.Sounds.Purchase)
else
-- 購入できない場合のエラーサウンド
Sound(v, Settings.Sounds.ErrorBuy)
end
end
end
end
end
end
end
end)
end
end)
end
購入関数
このPurchase関数は、プレイヤーがアイテムを購入したときに呼び出される関数です。
まず、プレイヤーのお金(stats.Value)からアイテムのコスト(cost)を引きます。
次に、購入したアイテムをPurchasedObjectsフォルダに移動します。
最後に、設定によってはボタン(item.Head)がフェードアウトします。これは視覚的なエフェクトで、アイテムが購入されたことをプレイヤーに知らせます。
-- Purchase関数:アイテムを購入する処理
function Purchase(tbl)
-- 引数から各種情報を取得
local cost = tbl[1] -- アイテムのコスト
local item = tbl[2] -- 購入するアイテム
local stats = tbl[3] -- プレイヤーのお金(またはステータス)
-- プレイヤーのお金を減らす
stats.Value = stats.Value - cost
-- 購入したアイテムをPurchasedObjectsに移動
Objects[item.Object.Value].Parent = script.Parent.PurchasedObjects
-- ボタンのフェードアウト設定が有効な場合
if Settings['ButtonsFadeOut'] then
-- 衝突を無効にする
item.Head.CanCollide = false
-- コルーチンでフェードアウト処理
coroutine.resume(coroutine.create(function()
for i=1,20 do
wait(Settings['FadeOutTime']/20)
item.Head.Transparency = item.Head.Transparency + 0.05
end
end))
else
-- フェードアウト設定が無効な場合、直接透明にする
item.Head.CanCollide = false
item.Head.Transparency = 1
end
end
オブジェクトの作成
このCreate関数は、新しい購入可能なオブジェクト(Model)を作成して、それをBuyObjectの子として追加します。
まず、新しいModelインスタンスを作成します。
次に、そのModelにNumberValueとObjectValueを追加します。これらはそれぞれコストとボタンに関する情報を保持します。
最後に、新しいObjectValueを作成して、その名前を"Stats"に設定します。これはプレイヤーのステータスに関する情報を保持します。
-- Create関数:新しい購入可能なオブジェクトを作成する
function Create(tab)
-- 新しいModelインスタンスを作成
local x = Instance.new('Model')
-- NumberValueを作成して、そのValueにtab[1](コスト)を設定
Instance.new('NumberValue', x).Value = tab[1]
-- そのNumberValueの名前を"Cost"に設定
x.Value.Name = "Cost"
-- ObjectValueを作成して、そのValueにtab[2](ボタン)を設定
Instance.new('ObjectValue', x).Value = tab[2]
-- そのObjectValueの名前を"Button"に設定
x.Value.Name = "Button"
-- 新しいObjectValue(名前は"Stats")を作成して、そのValueにtab[3](ステータス)を設定
local Obj = Instance.new('ObjectValue', x)
Obj.Name = "Stats"
Obj.Value = tab[3]
-- 作成したModelをBuyObjectの子として設定
x.Parent = script.Parent.BuyObject
end
GateControl
概要
- プレイヤーの確認: スクリプトはプレイヤーがゲート(Head)に触れたときに発火します。そのプレイヤーがゲーム内に存在するかどうかを確認します。
- プレイヤーのステータス確認: game.ServerStorage.PlayerMoney からプレイヤーのステータスを探します。
- 所有状態の確認: プレイヤーがすでに「Tycoon」を所有しているかどうかを確認します。
- 所有者の設定: もし「Tycoon」がまだ誰も所有していない場合、そのプレイヤーを新しい所有者として設定します。
- ゲートの属性変更: 所有者が設定された後、ゲート(Head)の透明度と衝突属性を変更します。
- チームカラーの設定: 新しい所有者のチームカラーを「Tycoon」のチームカラーに設定します。
コードセクションと解説
-- プレイヤーがゲート(Head)に触れたときにこの関数が呼び出されます
script.Parent.Head.Touched:connect(function(hit)
-- 触れたキャラクターからプレイヤーを取得
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
-- プレイヤーが存在する場合
if player ~= nil then
-- プレイヤーのステータス(PlayerMoney)をServerStorageから取得
local PlayerStats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name)
-- プレイヤーのステータスが存在する場合
if PlayerStats ~= nil then
-- プレイヤーが「Tycoon」を所有しているかどうかの情報を取得
local ownstycoon = PlayerStats:FindFirstChild("OwnsTycoon")
-- "OwnsTycoon" が存在する場合
if ownstycoon ~= nil then
-- まだ「Tycoon」を所有していない場合
if ownstycoon.Value == nil then
-- まだ誰も「Tycoon」を所有していない場合
if script.Parent.Parent.Parent.Owner.Value == nil then
-- 触れたキャラクターが生きている場合
if hit.Parent:FindFirstChild("Humanoid") then
if hit.Parent.Humanoid.Health > 0 then
-- 新しい所有者としてプレイヤーを設定
script.Parent.Parent.Parent.Owner.Value = player
ownstycoon.Value = script.Parent.Parent.Parent
-- ゲート(Head)の名前と透明度、衝突属性を更新
script.Parent.Name = player.Name.."'s Tycoon"
script.Parent.Head.Transparency = 0.6
script.Parent.Head.CanCollide = false
-- 新しい所有者のチームカラーを設定
player.TeamColor = script.Parent.Parent.Parent.TeamColor.Value
end
end
end
end
end
end
end
end)
まとめ
Zednov's Tycoon Kitの基本的な使い方と中身の構成について調べたことで、Tycoon作成のノウハウが少しわかってきました。
このKitではデータのクラウド保存はしてなさそうですので、よくあるデータを自動保存して、次回アクセス時にロードされるような仕組みは別途実装が必要になりそうです。
また、Core_handlerもWorkspaceに配置するモデルとなっているため、チート対策など考慮するとServerScriptService側へ配置できるように修正が必要かもしれません。
色々細かいところはありますが、大枠を理解する上でシンプルですごく良い教材でした。
ディレクトリ構成も参考にしつつ、自分のゲームに上手く取り入れながら開発を進めたいと思います。
長々とお付き合い頂きありがとうございます。
Discussion