no-img
سامنتا

آموزش ساخت بازی دو بعدی (جلسه ی پنجم)

یادگیری
تمرین
آینده سازی

سامنتا
دانلودهای ویژه
اطلاعیه های سایت

ادامه مطلب

آموزش ساخت بازی دو بعدی (جلسه ی پنجم)
zip
آبان ۴, ۱۳۹۵
دانلود آموزش های فارسی رایگان در کانال سامنتا

آموزش ساخت بازی دو بعدی (جلسه ی پنجم)


برای دیدن جزئیات تمام جلسه های این دوره اینجا کلیک کنید .

پیش از گذراندن این درس حتما دروس قبلی را مطالعه کنید . 

پیمایش بازی (Parallax scrolling)

ما قبلا بازی ساختیم با صحنه و پس زمینه ی ثابت که مطمئنا چنین حالتی خسته کننده خواهد بود بیایید کمی با پس زمینه ی کار کنیم و آنرا بهتر کنیم

چیزی که از ۱۵ سال پیش در تمام بازی های دوبعدی دیده میشود و پس زمینه ی بازی ها را به حالت متحرک یا نمایش پیمایشی در می آورد Parallax scrolling نام دارد 

ایده ای این کار به این صورت هست که اشیا پس زمینه ی بازی را با سرعت های مختلفی تکرار میکنیم تا برای کاربر این تداعی را داشته باشد که کاراکتر در حال حرکت مداوم هست 

اگر این کار به درستی انجام بگیرد یک عمق خیالی بسیار جالبی در بازی دو بعدی ایجاد خواهد کرد. 

 بیایید درون یونیتی این قابلیت را به بازی خودمان اضافه کنیم 

اضافه کردن پیمایش به بازی : 

اضافه کردن محور پیمایش نیازمند تفکر در مورد نحوه ی استفاده از این گزینه ی جدید میباشد 

همیشه بهتر هست قبل از عمل در مورد آن فکر کنید 🙂

ما چه کاری میخواهیم انجام دهیم ؟ 

  •  انتخاب اول : دوربین و کاراکتر حرکت بکنند و پس زمینه ثابت باشد 
  • انتخاب دوم : دوربین و کاراکتر ثابت باشد ولی مرحله بصورت تردمیل حرکت کند 

انتخاب اول ذهنیت خوبی نسبت به دوربین از نوع Perspective  ندارد چون که المان های پس زمینه عمق بالاتری دارند پس آنها که پست هستند احساس حرکت بسیار آرام را منتقل میکنند 

ولی در بازی دو بعدی استاندارد یونیتی ما از دوربین از نوع Orthographic  استفاده میکنیم که هیچ عمقی در کار نیست 

درباره ی دوربین باید بهتون بگم که خصوصیت Projection نوع دوربین را مشخص میکنه دو نوع دوربین داریم که دید مختلفی دارند 

یکی Prespective هست و همانطور که از اسمش پیداست عمق تصویر را نشان میدهد و برای بازی های سه بعدی مناسب هست و یک نوع Ortoghraphic داریم که هیچ عمقی ندارد و تمام اشیا را بدون در نظر گرفتن فاصله ی آنها از دوربین نمایش میدهد .

در نهایت برای اضافه کردن پیمایش به بازی خودمان باید از هر دو نوع بصورت مخلوطی استفاده کنیم که ما دو حالت اسکرول خواهیم داشت : 

  • کاراکتر در حال حرکت به سمت جلو هست به همراه دوربین
  • المان های پس زمینه با سرعت های مختلفی حرکت میکنند 

 

ریزش دشمن ها در بازی در یونیتی (Spawning )

اضافه کردن پیمایش به بازی عواقبی با خود دارد  مخصوصا در رابطه با دشمن های بازی . در حال حاضر دشمن های بازی با شروع بازی فقط حرکت میکنند و شلیک میکنند . ما میخواهیم آنها بدون آسیب دیدن منتظر بمانند تا ریزش شوند یا اصطلاحا spawn رخ بدهد. 

چطور میتوانیم دشمن را spawn کنیم ؟ 

این مورد قطعا به بازی شما بستگی دارد ،  شما میتوانید رخ دادهایی را برای  زمان spawn شدن دشمن ، موقعیت مکانی شروع آنها و دیگر موارد را به دلخواه خود انجام دهید .

اینجا کاری که ما انجام خواهیم داد : 

ما Poulpies  را بصورت مستقیم درون صحنه قرار میدهیم (بصورت مستقیم با درگ کردن prefab مربوط به آن) . بصورت پیشفرض آنها ثابت و بدون آسیب پذیری هستند تا زمانی که دوربین وارد آنها شود و آنها را فعال کند .

camera_use

بهترین ایده این هست که شما با استفاده از ادیتور یونیتی اقدام به تنظیم کاراکتر های دشمن خود بکنید 

plan ها:

ابتدا ، ما باید تعریف کنیم که plan ها اصلا چی هستند و آنها بصورت مداوم تکرار خواهند شد یا نه ؟ تکرار بکگراند باعث میشود که آن  بارها و بارها در طول مرحله پشت سر هم نمایش داده شود . 

برای مثال این مورد برای ساخت چیزی مثل یک آسمان بسیار پرکاربرد هست . 

ما چیزهایی که قرار هست داشته باشیم به شرح زیر هست :

تکرار شوندهلایه
بلهپس زمینه + آسمان
خیر پس زمینه (ردیف اول plan ها)
خیرپس زمینه (ردیف دوم plan ها)
خیر 

روی صحنه + بازیکن اصلی بازی + شخصیت دشمن ها

plan های بازی در یونیتی

 

ما باید تعدادی لایه به پس زمینه اضافه بکنیم 

شروع به کدزنی ، ساخت صحنه ی پیمایشی در بازی یونیتی (کدنویسی)

خب ، ما گفتیم که چطوری افکت پیمایش را به صحنه ی بازی خودمان اضافه کنیم . 

ولی برای فرا گرفتن این قسمت ها باید انها را عملا تمرین کنیم 

یونینی بصورت پیشفرض درون پکیج استاندارد خود (standard packages ) . شما میتوانید از آنها استفاده کنید ، ولی ما میخواهیم صحنه ی پیمایشی را اینجا از صفر بسازیم تا شما دقیقا یاد بگیرید !

اسکریپت نمونه 

با قسمت ساده ای شروع خواهیم کرد : پیمایش پس زمینه بدون حلقه ی تکرار . 

MoveScript را بخاطر بیاورید  . پایه ی هر دو یکی هست یک متغیر و مسیر به مرور اضافه خواهیم کرد .

اسکریپت جدید درست کنید و نام آنرا ScrollingScript قرار دهید ، محتویات آن باید به شکل زیر باشد : 

using UnityEngine;
/// <summary>
/// Parallax scrolling script that should be assigned to a layer
/// </summary>
public class ScrollingScript : MonoBehaviour
{ /// <summary> /// Scrolling speed /// </summary> public Vector2 speed = new Vector2(2, 2); /// <summary> /// Moving direction /// </summary> public Vector2 direction = new Vector2(-1, 0); /// <summary> /// Movement should be applied to camera /// </summary> public bool isLinkedToCamera = false; void Update() { // Movement Vector3 movement = new Vector3( speed.x * direction.x, speed.y * direction.y, ۰); movement *= Time.deltaTime; transform.Translate(movement); // Move the camera if (isLinkedToCamera) { Camera.main.transform.Translate(movement); } }
}

اسکریپت را به اشیای زیر با مواردی که در جدول زیر داده شده است اختصاص دهید : 

اسکریپت صحنه ی پیمایشی در یونیتی

 

برای قابل درک کردن نتیجه ی کار موارد زیر را انجام دهید : 

  •  یک Background دیگر بعد از دو تای قبلی اضافه کنید 
  • چندعدد platform کوچک به لایه ی Background elements اضافه کنید
  • platform به لایه ی Middleground اضافه کنید 
  • دشمن هایی را در سمت راست لایه ی Foreground اضافه کنید 

نتیحه به شکل زیر خواهد بود : 

صحنه ی پیمایشی در یونیتی

صحنه ی پیمایشی در یونیتی

بد نیست ! ولی ما میبینیم که دشمن ها حرکت کرده و شلیک میکنند زمانی که بیرون از دوربین هستند حتی قبل از این که آنها ریزش شوند .

علاوه بر این ، آنها زمانی که پلایر را رد میکنند بازیابی نمیشوند (زوم کنید و سمت چپ صحنه را ببینید . دشمن ها هنوز در حال حرکت هستند).

این مشکل را بعدا حل خواهیم کرد فعلا میخواهیم background  بی نهایت را مدیریت کنیم .

پس زمینه تکرار شونده ی بی نهایت 

به منظور ایجاد یک بکگراند تکرار شونده ی بی نهایت ، ما فقط باید به لایه ای زیرینی که در سمت چپ لایه ی بی نهایت قرار دارد نگاه بندازیم 

زمانی که این شی از لبه ی سمت چپ دوربین فراتر میرود ، ما آنرا به سمت راست لایه هدایت میکنیم 

آموزش پیمایش کاراکتر بازی دو بعدی در یونیتی

 

برای یک فیلد لایه به همراه تصویر ، توجه کنید که شما حداقل سایز برای پوشش کامل دوربین نیاز دارید ، بنابراین ما چیزی که فراتر از آن باشد را هرگز مشاهده نخواهیم کرد . اینجا سه قسمت آسمان هست ولی قراردادن آنها دلخواه هست 

براساس انعطاف پذیری و منابع سخت افزاری که بازی راطراحی میکنید تعادل را ایجاد کنید.

در این اینجا ، ایده ی ما اینست که تمام بچه های لایه را بگیریم و renderer آنها را بررسی کنیم 

ما به یه متد دستی نیاز داریم تا بتوانیم چک کنیم که اشیا درون دوربین هستند ما باید این را از درون خود wiki یونیتی پیدا کنیم . این نه یک کلاس هست و نه یک اسکریپت بلکه یک افزونه ی کلاس سی شارپ هست 

اسکریپت افزونه ی rendrer سی شارپ 

اسکریپت جدید به نام RendererExtensions.cs بسازید و محتویات زیر را کپی کنید : 

using UnityEngine;
public static class RendererExtensions
{ public static bool IsVisibleFrom(this Renderer renderer, Camera camera) { Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera); return GeometryUtility.TestPlanesAABB(planes, renderer.bounds); }
}

 

ما این متد را باید سمت چپ لایه ی بی نهایت فراخوانی بکنیم 

ScrollingScript کامل

اسکریپت ScrollingScript را در زیر مشاهده کنید (توصبحات در زیر داده شده است )
 

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary>
/// Parallax scrolling script that should be assigned to a layer
/// </summary>
public class ScrollingScript : MonoBehaviour
{ /// <summary> /// Scrolling speed /// </summary> public Vector2 speed = new Vector2(10, 10); /// <summary> /// Moving direction /// </summary> public Vector2 direction = new Vector2(-1, 0); /// <summary> /// Movement should be applied to camera /// </summary> public bool isLinkedToCamera = false; /// <summary> /// ۱ - Background is infinite /// </summary> public bool isLooping = false; /// <summary> /// ۲ - List of children with a renderer. /// </summary> private List<SpriteRenderer> backgroundPart; // ۳ - Get all the children void Start() { // For infinite background only if (isLooping) { // Get all the children of the layer with a renderer backgroundPart = new List<SpriteRenderer>(); for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); SpriteRenderer r = child.GetComponent<SpriteRenderer>(); // Add only the visible children if (r != null) { backgroundPart.Add(r); } } // Sort by position. // Note: Get the children from left to right. // We would need to add a few conditions to handle // all the possible scrolling directions. backgroundPart = backgroundPart.OrderBy( t => t.transform.position.x ).ToList(); } } void Update() { // Movement Vector3 movement = new Vector3( speed.x * direction.x, speed.y * direction.y, ۰); movement *= Time.deltaTime; transform.Translate(movement); // Move the camera if (isLinkedToCamera) { Camera.main.transform.Translate(movement); } // ۴ - Loop if (isLooping) { // Get the first object. // The list is ordered from left (x position) to right. SpriteRenderer firstChild = backgroundPart.FirstOrDefault(); if (firstChild != null) { // Check if the child is already (partly) before the camera. // We test the position first because the IsVisibleFrom // method is a bit heavier to execute. if (firstChild.transform.position.x < Camera.main.transform.position.x) { // If the child is already on the left of the camera, // we test if it's completely outside and needs to be // recycled. if (firstChild.IsVisibleFrom(Camera.main) == false) { // Get the last child position. SpriteRenderer lastChild = backgroundPart.LastOrDefault(); Vector3 lastPosition = lastChild.transform.position; Vector3 lastSize = (lastChild.bounds.max - lastChild.bounds.min); // Set the position of the recyled one to be AFTER // the last child. // Note: Only work for horizontal scrolling currently. firstChild.transform.position = new Vector3(lastPosition.x + lastSize.x, firstChild.transform.position.y, firstChild.transform.position.z); // Set the recycled child to the last position // of the backgroundPart list. backgroundPart.Remove(firstChild); backgroundPart.Add(firstChild); } } } } }
}

شماره های بالا به توضیحات زیر اشاره دارد 

۱ ما به یک متغیر public نیازز داریم تا looping را روشن کنیم و این از طریق inspector انجام گیرد 

۲ ما یک متغیر private نیاز داریم تا بچه های لایه را ذخیره کنیم منظور از بچه همان child هست 

۳ در متد Start() ما backgroundPart  را با لیست child هایی تنظیم کردیم  که rendrer دارد . به لطف LINQ ما انها را براساس پوزیشن x مرتب کردیم و آنها را در سمت چپ اولین عنصر ارایه ریختیم

۴ در متد Update() اگر isLooping روشن باشد (منظور true باشد ) اولین عنصری که در لیست backgroundPart  قرارگرفته است را فراخوانی کردیم ما تست کردیم اگر بطور کامل بیرون از دوربین باشد . اگر چنین اتفاقی رخ دهد ما موقعیت انرا تغییردادیم که بعد از اخرین child باشد در نهایت ما آنرا در آخر لیست backgroundPart قرار دادیم .

در واقع backgroundPart  دقیقا نماینده ی این هست که چه اتفاقی در صحنه بیوفتد 

بخاطر داشته باشید Is Looping را در اسکریپت ScrollingScript برای ۰ – Background در Inspector فعال کنید 

آموزش بازی سازی در یونیتی

بله بلاخره ما یک اسکریپت parallax scrolling پیاده سازی کردیم . 

بهبود اسکریپت بالا : 

بیایید اسکریپتی که نوشتیم را بروزرسانی کنیم 

دشمن نسخه ی ۲ بصورت spawn

ما قبلا گفتیم که دشمن ها تا زمانی که در محدوده ی دوربین نیستند باید غیرفعال باشند و هم چنین زمانی که از دوربین بطور کامل رد شدند باید پاک شوند

ما میخواهیم EnemyScript را بروزرسانی کنیم پس : 

۱ movement ، کلایدر و auto fire را غیرفعال کنید 

۲ چک کنید که rendrer داخل دوربین باشد 

۳ آنرا فعال کنید 

۴ وقتی که game object از دوربین خارج شد آنرا نابود کن 

در کدهای زیر شماره ها برای توضیحات هست 

using UnityEngine;
/// <summary>
/// Enemy generic behavior
/// </summary>
public class EnemyScript : MonoBehaviour
{ private bool hasSpawn; private MoveScript moveScript; private WeaponScript[] weapons; private Collider2D coliderComponent; private SpriteRenderer rendererComponent; void Awake() { // Retrieve the weapon only once weapons = GetComponentsInChildren<WeaponScript>(); // Retrieve scripts to disable when not spawn moveScript = GetComponent<MoveScript>(); coliderComponent = GetComponent<Collider2D>(); rendererComponent = GetComponent<SpriteRenderer>(); } // ۱ - Disable everything void Start() { hasSpawn = false; // Disable everything // -- collider coliderComponent.enabled = false; // -- Moving moveScript.enabled = false; // -- Shooting foreach (WeaponScript weapon in weapons) { weapon.enabled = false; } } void Update() { // ۲ - Check if the enemy has spawned. if (hasSpawn == false) { if (rendererComponent.IsVisibleFrom(Camera.main)) { Spawn(); } } else { // Auto-fire foreach (WeaponScript weapon in weapons) { if (weapon != null && weapon.enabled && weapon.CanAttack) { weapon.Attack(true); } } // ۴ - Out of the camera ? Destroy the game object. if (rendererComponent.IsVisibleFrom(Camera.main) == false) { Destroy(gameObject); } } } // ۳ - Activate itself. private void Spawn() { hasSpawn = true; // Enable everything // -- Collider coliderComponent.enabled = true; // -- Moving moveScript.enabled = true; // -- Shooting foreach (WeaponScript weapon in weapons) { weapon.enabled = true; } }
}

بازی را اجرا کنید بله این یک باگ هست 

MoveScript را غیرفعال کنید ببنید پلایر هرگز به حرکت دشمن ها نمی رسد تصویر زیر را چک کنید 

camera_moving_along

به خاطر بسپارید : ما باید ScrollingScript را باید به این لایه اضافه کنیم برای اینکه پلایر همراه با دوربین حرکت بکند 

ولی یک راه حل ساده هم وجود دارد : ScrollingScript را از لایه Foreground به پلایر اختصاص دهید

چرا این کار را الان انجام دادیم ؟ 

تنها چیزی که در این لایه حرکت میکند خودش هست و اسکریپتی به یک شی خاصی هم اختصاص داده نشده هست 

دکمه ی play را بزنید و مشاهده میکنید که به درستی کار میکند 

۱ تا زمانی که دشمن ها spawn نشده اند غیر فعال هستند (تا زمانی که در محدوده ی دوربین قرار نگیرند )

۲  زمانی که انها از دوربین رد میشوند ناپدید میشوند 

آموزش ساخت دشمن بازی در یونیتی

نگه داشتن کاراکتر اصلی بازی (player) در چهارچوب دوربین بازی 

شما باید توجه داشته باشید که تا الان کاراکتر اصلی بازی به دوربین و محدوده ی دوربین اختصاص داده نشده هست 

بازی را اجرا کنید و دکمه حرکت به چپ یا راست بزنید میبینید که کاراکتر بعد از مدتی از محدودذه ی دوربین خارج میشود 

برای حل این مشکل باید اسکریپت PlayerScript را باز کنید و کدهای زیر را به اخر متذد Update() اضافه کنید

void Update() { // ... // ۶ - Make sure we are not outside the camera bounds var dist = (transform.position - Camera.main.transform.position).z; var leftBorder = Camera.main.ViewportToWorldPoint( new Vector3(0, 0, dist) ).x; var rightBorder = Camera.main.ViewportToWorldPoint( new Vector3(1, 0, dist) ).x; var topBorder = Camera.main.ViewportToWorldPoint( new Vector3(0, 0, dist) ).y; var bottomBorder = Camera.main.ViewportToWorldPoint( new Vector3(0, 1, dist) ).y; transform.position = new Vector3( Mathf.Clamp(transform.position.x, leftBorder, rightBorder), Mathf.Clamp(transform.position.y, topBorder, bottomBorder), transform.position.z ); // End of the update method }

کدهای بالا ساده هستند 

ما لبه های دوربین را پیدا میکنیم و مطمعن میشویم که کاراکتر درون دوربین باشد همین 

خسته نباشید 

اگر جاهایی از آموزش گنگ و مبهم بود عذرخواهی میکنم و سعی میکنم هرچه سریعتر اموزش ویدیویی رو بزارم تا مشکلات برطرف شه و در نهایت با به اشتراگ گذاری سایت با دوستان خود از ما حمایت کنید 

با احترام 

 

 

 

 



درباره نویسنده

تیم سامنتا 73 نوشته در سامنتا دارد . مشاهده تمام نوشته های

دیدگاه ها


5 پاسخ به “آموزش ساخت بازی دو بعدی (جلسه ی پنجم)”

  1. abolfazl.n گفت:

    Microsoft Visual C++

    مشکل همینه چکار کنم خواستم حذف کنم دوباره نصب کنم اما از نسخه های ۲۰۰۵ تا ۲۰۱۴ رو دارم هم ۳۲ بیت هم ۶۴ کدومو حذف کنم

    میترسم همرو حذف کنم برنامه های دیگه کار نکنن

  2. abolfazl.n گفت:

    سلام.

    خواستم ارور رو بزارم و دیروز یونیتی رو نصب و خواستم بازی رو درست کنم

    حالا امروز یونیتی رو که باز میکنم بعد رو پروژه کلیک میکنم میپره بیرون و ارور میده :

    runtime error !

    r6002

    -floating point support not loaded

    امروز که سیستمو روشن کردم این طوری شد ولی دیروز مشکلی نداشت

    خواهش میکنم کمکم کنید

    • سلام تا جایی که بنده اطلاع دارم چنین خطایی مربوط به یونیتی نیست تمام خطاها یونیتی با کلمه ی یونیتی و همچنین مشکل واضح همراه هستند 

      ممکنه مشکل شما از ویندوزتون یا از برنامه ی Microsoft Visual C++ باشه که نصب کردید 

      پیشنهاد میکنم برنامه ها و ویندوز خودتون رو چک کنید و سپس اقدام به استفاده از یونیتی و کدهای وب سایت کنید

      با تشکر

  3. abolfazl.n گفت:

    ScrollingScript خط بیستو هشتم مشکل داره لطفا درست کنید

    • سلام . این کدها قبلا درون یونیتی تست شده و اینجا قرار گرفته در حال حاضر خطایی که برای شما نمایش میده رو اینجا بنویسید تا متوجه منظورتون بشیم و اصلاحات لازم رو انجام بدیم 

      با تشکر فراوان از شما دوست گرامی

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *