הגיבור
חלק מהיחידה ארכיטקטורת משחקים ב-Unity במסלול ה-Unity של Coddy. שיעור 41 מתוך 50.
בפרק הזה נבנה את Arena Survivor, המשחק האחרון במסלול. גלים של אויבים שוטפים אל תוך זירה מוקפת חומה, ואתם נלחמים בהם בחרב: חיילים פשוטים שהולכים לעברכם, וכדורים שממתינים ואז מזנקים. אחרי שמביסים גל נשאר חיזוק, HUD מציג את הבריאות שלכם ואת הגל, וכשהבריאות שלכם נגמרת הריצה מסתיימת והריצה הטובה ביותר שלכם נשמרת. במשחק נעשה שימוש בכלים של היחידה הזאת היכן שהם מתאימים: Enemy מופשט עם שני סוגים, IDamageable לכל דבר שאפשר לפגוע בו, הגלים כנכס ScriptableObject, פריטי איסוף מאחורי ממשק, HUD שמאזין רק לאירועי C#, ושמירה ב-JSON. כל שנים עשר הסקריפטים נמצאים בפרויקט מההתחלה. רק ל-Hero כבר יש קוד, ובכל שיעור מוסיפים עוד.
החרב של הגיבור פוגעת במה שנמצא לפניו, ולכן התנופה צריכה בדיקה של "לפנים". Vector3.Dot(a, b) מכפילה את החלקים המתאימים של שני וקטורים ומחברת אותם: a.x * b.x + a.y * b.y + a.z * b.z. עבור שני כיוונים באורך 1, התוצאה היא הקוסינוס של הזווית ביניהם: 1 כשהם מצביעים לאותו כיוון, 0.866 בזווית של 30 מעלות, 0.707 בזווית של 45 מעלות, 0 בזווית ישרה, ו-1- כשהם מצביעים לכיוונים מנוגדים. לכן "בטווח של 30 מעלות מהכיוון שאליו אני פונה" הופך ל"מכפלה סקלרית של לפחות 0.866", בלי לחשב זווית. רובוט הזקיף הזה מפעיל את האזעקה כשהשחקן נמצא בטווח של 10 יחידות ובתוך שדה הראייה שלו, שגודלו 30 מעלות:
public Transform player;
public float range = 10f;
void Update()
{
Vector3 toPlayer = player.position - transform.position;
toPlayer.y = 0f;
bool near = toPlayer.magnitude <= range;
bool inView = Vector3.Dot(transform.forward, toPlayer.normalized) >= 0.866f;
if (near && inView)
{
Debug.Log("Alarm!");
}
}לשני הכיוונים צריך להיות אורך 1. ל-transform.forward תמיד יש אורך כזה, אבל האורך של toPlayer הוא כמרחק, ולכן קודם מנרמלים אותו; אחרת, שחקן שנמצא במרחק כפול היה מניב מכפלה כפולה. ערך ה-y שלו מוגדר ל-0 לפני כן, כך ששחקן שנמצא על שביל גבוה יותר נחשב לפי המקום שבו הוא עומד במפה, ולא לפי הגובה שלו. הסימן מבחין בין החזית לגב: מכפלה גדולה מ-0 פירושה שהכיוון נמצא בחצי הקדמי, וקטנה מ-0 פירושה שהוא נמצא מאחור.
אתגר
בינוניהתחל את Arena Survivor עם החרב של הגיבור. תנועת ההליכה מוגדרת כך: פעולת Move מזיזה את הגיבור באמצעות SimpleMove ומסובבת אותו כך שיפנה לכיוון ההליכה. חמישה בובות אימון עם התג Enemy עומדות סביבו. ה-Inspector מגדיר עבור Hero את reach (2) ואת swingCooldown (0.5), וכן את החיתוך שמוצג בזמן הנפה.
- הקשה על Space או לחיצה שמאלית מניפה את החרב, לכל היותר פעם אחת בכל
swingCooldownשניות: לחיצה לפני שההמתנה מסתיימת לא עושה דבר. קרא את המקשים באמצעותKeyboard.currentו-Mouse.current, וקרא ל-Swing(). - בתוך
Swing(), לאחר הצגת החיתוך, השמד כל אובייקט עם התגEnemyשנמצא בטווחreachולפני הגיבור. כל collider ש-Physics.OverlapSphereמוצא סביב הגיבור ברדיוסreachנמצא בטווח. אובייקט נמצא מלפנים אם המכפלה הסקלרית בין הכיוון הקדמי של הגיבור לבין הכיוון אל האובייקט לאורך הקרקע היא לפחות 0.5: עד 60 מעלות לכל אחד מהצדדים.
השאר את שאר הסקריפטים כפי שהם. לחץ על Play, גש אל בובות האימון והנף את החרב, ואז לחץ על Check.
נסו בעצמכם
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
public class Hero : MonoBehaviour
{
[SerializeField] private float speed = 5f;
[SerializeField] private float reach = 2f;
[SerializeField] private float swingCooldown = 0.5f;
[SerializeField] private GameObject slash;
private CharacterController controller;
private InputAction moveAction;
void Start()
{
controller = GetComponent<CharacterController>();
moveAction = InputSystem.actions.FindAction("Move");
}
void Update()
{
Vector2 input = moveAction.ReadValue<Vector2>();
Vector3 move = new Vector3(input.x, 0f, input.y);
controller.SimpleMove(move * speed);
if (move != Vector3.zero)
{
transform.rotation = Quaternion.LookRotation(move);
}
// לחיצה על Space או לחיצה שמאלית מבצעת הנפה, לכל היותר פעם אחת בכל swingCooldown שניות
}
void Swing()
{
StartCoroutine(ShowSlash());
// השמד כל דבר עם התג Enemy שנמצא בטווח ההגעה ומול הגיבור
}
IEnumerator ShowSlash()
{
slash.SetActive(true);
yield return new WaitForSeconds(0.1f);
slash.SetActive(false);
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה ארכיטקטורת משחקים ב-Unity
1רכיבים, לא היררכיות
רכיבים קטניםסקריפט בסיס לאויברכיבים מופשטיםרכיבים שעובדים יחדסיכום: צריח מודולרי