Menu
CoddyTech

הגיבור

חלק מהיחידה ארכיטקטורת משחקים ב-Unity במסלול ה-Unity של Coddy. שיעור 50 מתוך 64.

בפרק הזה נבנה את 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 פירושה שהוא נמצא אי־שם מאחור.

challenge icon

אתגר

בינוני

תן לגיבור חרב.

  1. ב־Update(), כשלוחצים על Space או מבצעים לחיצה שמאלית ו־Time.time >= nextSwing, הגדר nextSwing = Time.time + swingCooldown; וקרא ל־Swing().
  2. ב־Swing(), עבור בלולאה על Physics.OverlapSphere(transform.position, reach).
  3. השמד כל פגיעה שמתויגת Enemy ושהכיוון השטוח שלה, toHit (כאשר y מוגדר ל־0), מקיים Vector3.Dot(transform.forward, toHit.normalized) >= 0.5f.

לחץ על "שחק" והנף את החרב: הבובות שמלפנים נעלמות. ואז לחץ על "בדיקה".

נסו בעצמכם

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;
    private float nextSwing;

    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);
        }

        // לחיצה על מקש הרווח או לחיצה שמאלית מבצעת הנפה, לכל היותר פעם אחת בכל swingCooldown שניות
    }

    void Swing()
    {
        StartCoroutine(ShowSlash());
        // השמד את כל מה שמסומן בתג Enemy ונמצא בטווח ההגעה ומול הגיבור
    }

    IEnumerator ShowSlash()
    {
        slash.SetActive(true);
        yield return new WaitForSeconds(0.1f);
        slash.SetActive(false);
    }
}

כל השיעורים ביחידה ארכיטקטורת משחקים ב-Unity

תרגלו בעצמכם: Unity אונליין