การเปลี่ยนฉากใหม่ ใน Game Code Unity สำหรับการชนวัตถุเพื่อเปลี่ยนฉากและส่งค่าคะแนนไปยังฉากถัดไป
แน่นอนครับ นี่คือโค้ด Unity สำหรับการชนวัตถุ (เช่น ประตู) เพื่อเปลี่ยนฉากและส่งค่าคะแนนไปยังฉากถัดไป

สคริปต์สำหรับประตู (DoorTrigger.cs)
สร้างสคริปต์ C# ใหม่ชื่อ DoorTrigger.cs
แล้วคัดลอกโค้ดด้านล่างนี้ไปวาง
C#
using UnityEngine;
using UnityEngine.SceneManagement; // ต้องมี namespace นี้สำหรับ SceneManager
public class DoorTrigger : MonoBehaviour
{
public string nextSceneName; // ชื่อของฉากถัดไปที่จะโหลด
// ตั้งค่าตัวแปร static เพื่อเก็บคะแนนชั่วคราวระหว่างฉาก
// static ทำให้เข้าถึงได้จากที่ไหนก็ได้และข้อมูลยังอยู่แม้ฉากจะถูกทำลายไปแล้ว
public static int scoreFromPreviousScene = 0;
// ฟังก์ชันนี้จะถูกเรียกเมื่อมี Collider อื่นเข้ามาชนกับ Collider ของวัตถุนี้
// วัตถุที่มาชนจะต้องมี Rigidbody และ isTrigger ของ Collider จะต้องถูกติ๊ก
void OnTriggerEnter(Collider other)
{
// ตรวจสอบว่าวัตถุที่เข้ามาชนคือ "ผู้เล่น" ของเราหรือไม่
// คุณสามารถเปลี่ยน Tag นี้ให้เป็น Tag ของผู้เล่นของคุณได้ (เช่น "Player")
if (other.CompareTag("Player"))
{
// ดึงสคริปต์ที่เก็บคะแนนของผู้เล่นมา
// สมมติว่าสคริปต์คะแนนชื่อ PlayerScore และอยู่บน GameObject ผู้เล่น
PlayerScore playerScores = other.GetComponent<PlayerScore>();
if (playerScores != null)
{
// เก็บค่าคะแนนของผู้เล่นลงในตัวแปร static
scoreFromPreviousScene = playerScores.currentScore;
Debug.Log("คะแนนที่ส่งไปฉากหน้า: " + scoreFromPreviousScene);
}
else
{
Debug.LogWarning("ไม่พบสคริปต์ PlayerScore บนผู้เล่น! จะเปลี่ยนฉากโดยไม่มีคะแนน");
}
// โหลดฉากถัดไปตามชื่อที่กำหนด
SceneManager.LoadScene(nextSceneName);
}
}
}
สคริปต์สำหรับคะแนนผู้เล่น (PlayerScore.cs)
สร้างสคริปต์ C# ใหม่ชื่อ PlayerScore.cs
แล้วคัดลอกโค้ดด้านล่างนี้ไปวาง สคริปต์นี้จะอยู่บน GameObject ของผู้เล่นของคุณ
C#
using UnityEngine;
using TMPro; // ถ้าใช้ TextMeshPro สำหรับแสดงคะแนน (ต้อง Import TextMeshPro Assets ก่อน)
// หรือใช้ UnityEngine.UI; ถ้าใช้ UI.Text ปกติ
public class PlayerScore : MonoBehaviour
{
public int currentScore = 0; // คะแนนปัจจุบันของผู้เล่น
public TextMeshProUGUI scoreText; // อ้างอิงถึง UI Text ที่จะแสดงคะแนน (ถ้าใช้ TextMeshPro)
// public Text scoreText; // ถ้าใช้ UI.Text ปกติ
void Start()
{
// เมื่อฉากใหม่โหลดขึ้นมา ให้ตรวจสอบว่ามีคะแนนส่งมาจากฉากก่อนหน้าหรือไม่
// และเพิ่มคะแนนนั้นเข้ากับคะแนนเริ่มต้นในฉากปัจจุบัน
if (DoorTrigger.scoreFromPreviousScene > 0)
{
currentScore += DoorTrigger.scoreFromPreviousScene;
Debug.Log("ได้รับคะแนนจากฉากก่อนหน้า: " + DoorTrigger.scoreFromPreviousScene);
// หลังจากใช้งานแล้ว ให้รีเซ็ตค่า static เพื่อป้องกันการนำไปใช้ซ้ำโดยไม่ตั้งใจ
DoorTrigger.scoreFromPreviousScene = 0;
}
UpdateScoreUI(); // อัปเดต UI แสดงคะแนนเมื่อเริ่มเกม
}
// ฟังก์ชันสำหรับเพิ่มคะแนน
public void AddScore(int amount)
{
currentScore += amount;
UpdateScoreUI();
}
// ฟังก์ชันสำหรับอัปเดต UI แสดงคะแนน
void UpdateScoreUI()
{
if (scoreText != null)
{
scoreText.text = "คะแนน: " + currentScore;
}
}
// ตัวอย่างการเพิ่มคะแนน (คุณสามารถเรียกใช้จากที่อื่นได้ เช่น เมื่อเก็บไอเทม)
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Coin")) // สมมติว่ามีเหรียญ Tag "Coin"
{
AddScore(10); // เพิ่ม 10 คะแนนเมื่อชนเหรียญ
Destroy(other.gameObject); // ทำลายเหรียญที่เก็บไป
}
}
}
ขั้นตอนการตั้งค่าใน Unity
- สร้างฉาก (Scenes):
- สร้างฉากของคุณ เช่น “Level1” และ “Level2” (หรือ “NextLevel” ตามที่คุณต้องการ)
- สำคัญมาก: ไปที่ File > Build Settings… แล้วลากฉาก “Level1” และ “Level2” เข้าไปใน “Scenes In Build” เพื่อให้ Unity รู้จักฉากเหล่านี้
- ตั้งค่า GameObject ของประตู:
- สร้าง GameObject ที่เป็นตัวแทนของประตูของคุณ (เช่น Cube, 3D Model)
- เพิ่ม Component Box Collider (หรือ Collider อื่นๆ ที่เหมาะสมกับรูปร่างประตู)
- ติ๊กช่อง “Is Trigger” ใน Box Collider component
- เพิ่ม Component Rigidbody ให้กับประตู (สามารถติ๊ก “Is Kinematic” ได้ถ้าไม่ต้องการให้ประตูถูกแรงภายนอกกระทบ)
- ลากสคริปต์
DoorTrigger.cs
ไปใส่ใน GameObject ประตู - ใน Inspector ของประตู จะมีช่อง “Next Scene Name” ให้คุณใส่ชื่อฉากถัดไปที่คุณต้องการให้โหลด (เช่น “Level2”)
- ตั้งค่า GameObject ของผู้เล่น:
- ตรวจสอบให้แน่ใจว่าผู้เล่นของคุณมี Collider (เช่น Capsule Collider) และ Rigidbody
- ตั้งค่า Tag ของผู้เล่น: เลือก GameObject ผู้เล่นของคุณ ใน Inspector ตรงส่วนบนสุดจะมีช่อง “Tag” ให้เลือก “Add Tag…” แล้วสร้าง Tag ใหม่ชื่อ “Player” จากนั้นกลับมาที่ผู้เล่นแล้วเลือก Tag “Player” ให้กับมัน (สคริปต์
DoorTrigger
จะใช้ Tag นี้เพื่อตรวจสอบว่าใครมาชน) - ลากสคริปต์
PlayerScore.cs
ไปใส่ใน GameObject ผู้เล่น - ตั้งค่า UI แสดงคะแนน (ถ้ามี):
- สร้าง Text UI ใน Canvas ของคุณ (GameObject > UI > Text – TextMeshPro หรือ Text)
- ใน Inspector ของ
PlayerScore
บนผู้เล่น จะมีช่อง “Score Text” ให้คุณลาก Text UI ที่สร้างไว้มาใส่
- ทดสอบ:
- ลองเล่นเกมของคุณในฉาก “Level1”
- เพิ่มคะแนนให้ผู้เล่น (อาจจะใช้สคริปต์
PlayerScore
เพิ่มคะแนนเมื่อเก็บไอเทม หรือแค่เพิ่มค่าcurrentScore
ใน Inspector ตอนเล่น) - เดินผู้เล่นไปชนประตู
- เกมควรจะเปลี่ยนไปที่ฉาก “Level2” และในฉาก “Level2” คะแนนของผู้เล่นควรจะแสดงผลรวมจากฉากก่อนหน้าด้วย
คำอธิบายเพิ่มเติม:
OnTriggerEnter(Collider other)
: เป็นฟังก์ชันของ Unity ที่จะถูกเรียกโดยอัตโนมัติเมื่อ Collider ของวัตถุนี้ (ประตู) ชนกับ Collider ของวัตถุอื่น (other
) โดยที่ Collider ของประตูจะต้อง Is Trigger และวัตถุที่มาชนจะต้องมี Rigidbodyother.CompareTag("Player")
: เป็นวิธีที่ปลอดภัยและมีประสิทธิภาพในการตรวจสอบว่าวัตถุที่เข้ามาชนมี Tag ที่เรากำหนดไว้หรือไม่ การใช้CompareTag
ดีกว่าการใช้other.tag == "Player"
ตรงที่มันจะหลีกเลี่ยงการสร้าง Allocation ในหน่วยความจำSceneManager.LoadScene(nextSceneName)
: นี่คือคำสั่งสำหรับโหลดฉากใหม่ตามชื่อที่ระบุpublic static int scoreFromPreviousScene
: นี่คือหัวใจของการส่งข้อมูลข้ามฉาก.public
: ทำให้เข้าถึงได้จากสคริปต์อื่นstatic
: นี่คือส่วนสำคัญที่สุด ทำให้scoreFromPreviousScene
เป็นตัวแปรที่ “เป็นของคลาส” (DoorTrigger
) ไม่ใช่ “เป็นของ Instance” (วัตถุประตูแต่ละบาน) และข้อมูลนี้จะยังคงอยู่ในหน่วยความจำแม้ว่าฉากปัจจุบันจะถูกทำลายไปแล้วเมื่อมีการเปลี่ยนฉาก ทำให้ฉากถัดไปสามารถเข้าถึงค่านี้ได้- เมื่อฉากใหม่โหลดขึ้นมา สคริปต์
PlayerScore
ในฉากใหม่จะเข้าไปอ่านค่าจากDoorTrigger.scoreFromPreviousScene
เพื่อดึงคะแนนที่ส่งมา - ควรตั้งค่า
DoorTrigger.scoreFromPreviousScene = 0;
หลังใช้งาน: เพื่อป้องกันไม่ให้คะแนนเดิมถูกนำไปใช้ซ้ำหากมีการเปลี่ยนฉากอีกครั้งในอนาคตโดยไม่ได้ตั้งใจ
หวังว่าโค้ดและคำอธิบายนี้จะเป็นประโยชน์ในการพัฒนาเกม Unity ของคุณนะครับ! หากมีคำถามเพิ่มเติม ถามได้เลยครับ
วิธีแก้ไข ScoreManager.currentScore’ is inaccessible due to its protection level
ข้อความ “ScoreManager.currentScore’ is inaccessible due to its protection level” หมายความว่าคุณกำลังพยายามเข้าถึงตัวแปร currentScore
ในคลาส ScoreManager
แต่ตัวแปรนั้นไม่ได้ถูกตั้งค่าให้สามารถเข้าถึงได้จากภายนอกคลาสครับ
ปัญหานี้มักเกิดจากตัวแปรนั้นเป็น private
หรือไม่ได้ระบุ public
หรือ [SerializeField]
ไว้
นี่คือวิธีแก้ไขครับ:
1. ทำให้ currentScore
เป็น public
วิธีที่ง่ายที่สุดคือการทำให้ตัวแปร currentScore
ในคลาส ScoreManager
เป็น public
ครับ
ในไฟล์ ScoreManager.cs
ของคุณ:
C#
public class ScoreManager : MonoBehaviour
{
// แก้ไขตรงนี้: เพิ่ม 'public' เข้าไป
public int currentScore = 0;
// ... ส่วนอื่นๆ ของโค้ด ScoreManager
}
เมื่อคุณประกาศตัวแปรเป็น public
สคริปต์อื่น ๆ จะสามารถเข้าถึงและแก้ไขค่า currentScore
ได้โดยตรง เช่น ScoreManager.currentScore = 100;
หรือ int score = ScoreManager.currentScore;
2. ใช้ Property (แนะนำสำหรับโครงสร้างที่ดีขึ้น)
ถ้าคุณอยากให้โค้ดมีความเป็นระเบียบและควบคุมการเข้าถึงได้ดีขึ้น คุณสามารถใช้ Property ได้ครับ
ในไฟล์ ScoreManager.cs
ของคุณ:
C#
public class ScoreManager : MonoBehaviour
{
private int _currentScore = 0; // เปลี่ยนเป็น private และมี underscore นำหน้า (ตามหลักปฏิบัติที่ดี)
// สร้าง Public Property สำหรับเข้าถึงคะแนน
public int CurrentScore
{
get { return _currentScore; } // อนุญาตให้อ่านค่า
set { _currentScore = value; } // อนุญาตให้กำหนดค่า
}
// หรือถ้าอยากให้แค่ "อ่าน" ได้อย่างเดียวจากภายนอก แต่ "กำหนดค่า" ได้เฉพาะในคลาสนี้เท่านั้น
// public int CurrentScore { get; private set; } // สำหรับ C# 6.0 ขึ้นไป
// ... ส่วนอื่นๆ ของโค้ด ScoreManager
}
วิธีการเรียกใช้จากสคริปต์อื่น:
C#
// สมมติว่าคุณมี reference ไปยัง ScoreManager หรือเป็น Singleton
ScoreManager.instance.CurrentScore = 100; // กำหนดค่า
int score = ScoreManager.instance.CurrentScore; // อ่านค่า
การใช้ Property ช่วยให้คุณสามารถเพิ่ม logic เข้าไปในการอ่านหรือเขียนค่าได้ เช่น การอัปเดต UI ทุกครั้งที่คะแนนเปลี่ยน หรือการตรวจสอบค่าคะแนนก่อนที่จะตั้งค่า
3. ใช้เมธอด public
สำหรับการเข้าถึง/แก้ไข (ถ้าต้องการควบคุมที่ละเอียดกว่า)
หากคุณต้องการควบคุมการเข้าถึง currentScore
อย่างละเอียด และไม่อนุญาตให้สคริปต์อื่นเข้าถึงตัวแปรโดยตรง แต่ให้ผ่านฟังก์ชันเท่านั้น คุณสามารถสร้างเมธอด public
สำหรับการเพิ่ม/ลด หรือรับค่าคะแนนได้ครับ
ในไฟล์ ScoreManager.cs
ของคุณ:
C#
public class ScoreManager : MonoBehaviour
{
private int currentScore = 0; // เก็บเป็น private
// เมธอดสำหรับเพิ่มคะแนน
public void AddScore(int amount)
{
currentScore += amount;
// อาจจะเรียกฟังก์ชันอัปเดต UI ตรงนี้ด้วย
}
// เมธอดสำหรับลดคะแนน
public void DeductScore(int amount)
{
currentScore -= amount;
// อาจจะเรียกฟังก์ชันอัปเดต UI ตรงนี้ด้วย
}
// เมธอดสำหรับดึงค่าคะแนนปัจจุบัน
public int GetCurrentScore()
{
return currentScore;
}
// ... ส่วนอื่นๆ ของโค้ด ScoreManager
}
วิธีการเรียกใช้จากสคริปต์อื่น:
C#
// สมมติว่าคุณมี reference ไปยัง ScoreManager หรือเป็น Singleton
ScoreManager.instance.AddScore(50); // เพิ่มคะแนน
int score = ScoreManager.instance.GetCurrentScore(); // อ่านคะแนน
สรุป:
- ถ้าคุณต้องการเข้าถึง
currentScore
โดยตรงจากที่อื่น: ทำให้เป็นpublic int currentScore;
- ถ้าคุณต้องการโครงสร้างที่ดีขึ้น และอาจมี logic เพิ่มเติมในการเข้าถึง: ใช้
public int CurrentScore { get; set; }
(Property) - ถ้าคุณต้องการควบคุมการเปลี่ยนแปลงคะแนนผ่านฟังก์ชันเท่านั้น: ใช้เมธอด
public void AddScore(int amount)
และpublic int GetCurrentScore()
เลือกวิธีที่เหมาะสมกับความต้องการและโครงสร้างโปรเจกต์ของคุณได้เลยครับ วิธีที่ 1 คือวิธีแก้ปัญหาที่ตรงไปตรงมาที่สุด ส่วนวิธีที่ 2 และ 3 จะช่วยให้โค้ดของคุณเป็นระเบียบและจัดการได้ง่ายขึ้นในระยะยาวครับ
แต่ถ้าตัวแปร currentScore
ถูกประกาศเป็น public
อยู่แล้ว (public int currentScore = 0;
) ซึ่งหมายความว่ามันสามารถเข้าถึงได้จากภายนอกคลาสครับ
ดังนั้น ข้อความแจ้งเตือน “‘ScoreManager.currentScore’ is inaccessible due to its protection level” ไม่ได้เกิดจากโค้ด ScoreManager
แต่เกิดจาก โค้ดในสคริปต์อื่น (เช่น DoorTrigger.cs
หรือสคริปต์ที่เคยเป็น PlayerScore.cs
ก่อนหน้านี้) ที่พยายามจะเข้าถึง currentScore
ผิดวิธี ครับ
เนื่องจากได้นำ Singleton Pattern มาใช้กับ ScoreManager
แล้ว (public static ScoreManager Instance { get; private set; }
) คุณจะต้องเข้าถึง currentScore
ผ่าน Instance
ของ ScoreManager
เสมอครับ
วิธีแก้ไขคือ:
คุณต้องไปแก้ไขโค้ดในสคริปต์ที่พยายามจะเข้าถึงคะแนน (เช่น DoorTrigger.cs
) ให้เรียกใช้ผ่าน ScoreManager.Instance
ครับ
ตัวอย่างการแก้ไขในสคริปต์ DoorTrigger.cs
(หรือสคริปต์อื่นที่เกี่ยวข้องกับการส่งคะแนน):
C#
using UnityEngine;
using UnityEngine.SceneManagement;
public class DoorTrigger : MonoBehaviour
{
public string nextSceneName; // ชื่อของฉากถัดไปที่จะโหลด
// ตั้งค่าตัวแปร static เพื่อเก็บคะแนนชั่วคราวระหว่างฉาก
// static ทำให้เข้าถึงได้จากที่ไหนก็ได้และข้อมูลยังอยู่แม้ฉากจะถูกทำลายไปแล้ว
public static int scoreFromPreviousScene = 0;
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
// *** แก้ไขตรงนี้: เข้าถึงคะแนนผ่าน ScoreManager.Instance ***
// ตรวจสอบว่า ScoreManager.Instance ไม่เป็น null ก่อนใช้งาน
if (ScoreManager.Instance != null)
{
scoreFromPreviousScene = ScoreManager.Instance.currentScore; // ดึงคะแนนจาก ScoreManager
Debug.Log("คะแนนที่ส่งไปฉากหน้า: " + scoreFromPreviousScene);
}
else
{
Debug.LogWarning("ไม่พบ ScoreManager.Instance! จะเปลี่ยนฉากโดยไม่มีคะแนน");
}
// โหลดฉากถัดไปตามชื่อที่กำหนด
SceneManager.LoadScene(nextSceneName);
}
}
}
คำอธิบายการเปลี่ยนแปลง:
- จากเดิมที่อาจจะพยายามเข้าถึง
PlayerScore.currentScore
(ถ้ายังมีสคริปต์PlayerScore
อยู่) หรือScoreManager.currentScore
โดยตรง (ซึ่งจะใช้ไม่ได้เพราะcurrentScore
ไม่ใช่static
ในScoreManager
แต่เป็นตัวแปรของInstance
) - เราเปลี่ยนมาใช้
ScoreManager.Instance.currentScore
แทน เพื่อเข้าถึงตัวแปรcurrentScore
ผ่านInstance
ของScoreManager
ที่ถูกสร้างขึ้นมาเพียงครั้งเดียวในเกม
สรุปขั้นตอนการตรวจสอบและแก้ไข:
- ตรวจสอบ
ScoreManager.cs
: โค้ดที่คุณให้มาถูกต้องแล้วสำหรับScoreManager
ที่เป็น Singleton และมีcurrentScore
เป็นpublic
- แก้ไขสคริปต์
DoorTrigger.cs
: อัปเดตตามโค้ดด้านบน เพื่อให้ดึงคะแนนจากScoreManager.Instance.currentScore
- ตรวจสอบสคริปต์อื่นๆ (ถ้ามี): หากคุณมีสคริปต์อื่นที่เคยเข้าถึงคะแนนของผู้เล่นโดยตรง (เช่น สคริปต์ที่เคยเป็น
PlayerScore.cs
หรือสคริปต์ที่เพิ่ม/ลดคะแนน) ให้เปลี่ยนมาเรียกใช้เมธอดChangeScore
ของScoreManager
หรือเข้าถึงScoreManager.Instance.currentScore
โดยตรง เช่น:C#// ตัวอย่างการเพิ่มคะแนนจากสคริปต์อื่น if (ScoreManager.Instance != null) { ScoreManager.Instance.ChangeScore(10); // เพิ่ม 10 คะแนน }
หลังจากแก้ไขแล้ว ข้อผิดพลาด “inaccessible due to its protection level” ควรจะหายไปครับ