블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2858)
Unity3D (897)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (192)
협업 (64)
3DS Max (3)
Game (12)
Utility (142)
Etc (99)
Link (34)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (16)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (19)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

StreamingAssets 폴더는 Platform마다 다르다.

게다가 더 기가막힌게, Application.streamingAssetsPath를 그대로 믿고 사용할 수 없다는 것이다.

게다가 Unity 문서는 지나간 내용을 설명하고 있으며, 잘못된 내용을 설명하고 있다.

http://docs.unity3d.com/kr/current/Manual/StreamingAssets.html

이런~~~~


정답은 아래와 같다.

#if UNITY_EDITOR || UNITY_STANDALONE_WIN

public static readonly string StreamingPath = "file://" + Application.streamingAssetsPath + "/";

#elif UNITY_ANDROID

public static readonly string StreamingPath = Application.streamingAssetsPath + "/";

#elif UNITY_IOS

public static readonly string StreamingPath = "file://" + Application.streamingAssetsPath + "/";

#endif




[출처] http://baramlife.tistory.com/7

반응형
Posted by blueasa
, |

파티클 이펙트를 런타임 중에 방향을 반전 시키는 간단한 스크립트.


생성 될 때 방향을 반전시키기 위해서 ParticleSystem.startSpeed 를 반전 시키고,

이미 생성 된 파티클은 ParticleSystem.Particle.velocity 를 반전 시켜 줌.



using UnityEngine;
using System.Collections;

public class InvertParticleEffect : MonoBehaviour
{
    ParticleSystem m_System;
    ParticleSystem.Particle[] m_Particles;


    void InitializeIfNeeded()
    {
        if (m_System == null)
            m_System = GetComponent();

        if (m_Particles == null || m_Particles.Length < m_System.maxParticles)
            m_Particles = new ParticleSystem.Particle[m_System.maxParticles];
    }

    public void Invert()
    {
        InitializeIfNeeded();

        // 생성될 때, 파티클 Speed 방향 반전
        var vel = m_System.startSpeed *= -1f;

        // GetParticles is allocation free because we reuse the m_Particles buffer between updates
        int numParticlesAlive = m_System.GetParticles(m_Particles);

        // Change only the particles that are alive
        for (int i = 0; i < numParticlesAlive; i++)
        {
            // 이미 생성된 파티클은 velocity 반전해서 반대로 가도록..
            m_Particles[i].velocity *= -1f;
        }

        // Apply the particle changes to the particle system
        m_System.SetParticles(m_Particles, numParticlesAlive);
    }

}

[참조] https://docs.unity3d.com/ScriptReference/ParticleSystem.GetParticles.html


반응형
Posted by blueasa
, |
Best Answer

Answer by clunk47 

Of course it's easy. I do it by creating a shortcut or batch file that starts the exe, instead of running the exe file. For example, a batch file. This is just an example, so say our EXE name is RUN.EXE. You would open Notepad, type the following:

start RUN.EXE -popupwindow

Then File> Save as> and in the filename where it says .txt originally, just remove that and use an asterick and a bat extension (*.bat) Hit ENTER, then it will know you want to save as a bat file. Now just type in a name like Launcher.bat, don't forget to remove the asterick but keep the .bat extension. Now you would run this Launcher file. The argument -popupwindow is what allows your Unity Standalone EXE to run without borders :D




[출처] http://answers.unity3d.com/questions/13009/borderless-window-in-standalone-player.html

반응형
Posted by blueasa
, |

Welcome to the "Borderless Resizable Movable Standalone Window" Unity Asset.

Summary:
"Borderless Resizable Movable Standalone Window" is an Unity Plugin that gives you the ability to control the actual window position,size and borders of your standalone build via Scripting.
In other words it lets you remove the borders of your game's window and gives you the API to minimize,close,maximize,resize ect. through your scripts.
There are tons of creative ways to use it and implement it to your games/programs an example would be this template.
The package comes with 2 Example Scenes , 1 Launch Scene and 4 scripts (C# and Js) to help you start and understand its use.
Can be combined with Unity's current API to achieve great effects !
NOTE : It has been tested on Windows Unity Pro and Indie, other platforms are unknown!
So if you purchase for another platform do it at your own risk !!

What you actually get:
~A "QuickAutoBorderless" boolean that forces Borderless mode on the build on initialization without restarting it
~A "FullyAutoBorderless" boolean that forces Borderless mode on the build on initialization after restarting it
~A "AllowSizeResettingBeforeExit" boolean that makes the window start as a small 116x90 window instead of full size
~New Scripting API.
-Window.Minimize ();
-Window.Fullscreen ();
-Window.Fullscreen (Vector2);
-Window.Fullscreen (int,int);
-Window.Maximize ();
-Window.Maximize (boolean);
-Window.Exit ();
-Window.ForceExit ();
-Window.IsDoneLoading();
-Window.IsBorderless ();
-Window.Border ();
-Window.Border (boolean);
-Window.SetMinWidth (int);
-Window.SetMaxWidth (int);
-Window.SetMinHeight (int);
-Window.SetMaxHeight (int);
-Window.SetRect (Rect);
-Window.SetRect (int,int,int,int);
-Window.SetPosition (int,int);
-Window.SetPosition (Vector2);
-Window.SetSize (int,int);
-Window.SetSize (Vector2);
-Window.GetMinWidth ();
-Window.GetMaxWidth ();
-Window.GetMinHeight ();
-Window.GetMaxHeight ();
-Window.GetBorderRect ();
-Window.GetBorderPosition ();
-Window.GetBorderSize ();
-Window.GetRect ();
-Window.GetPosition ();
-Window.GetSize ();
-Window.GrabStart ();
-Window.GrabEnd ();
-Window.QuickDisableBorders();
-Window.ResizeLeftStart ();
-Window.ResizeLeftEnd ();
-Window.ResizeDownLeftStart ();
-Window.ResizeDownLeftEnd ();
-Window.ResizeDownStart ();
-Window.ResizeDownEnd ();
-Window.ResizeDownRightStart ();
-Window.ResizeDownRightEnd ();
-Window.ResizeRightStart ();
-Window.ResizeRightEnd ();
-Window.ResizeRightTopStart ();
-Window.ResizeRightTopEnd ();
-Window.ResizeTopStart ();
-Window.ResizeTopEnd ();
-Window.ResizeTopLeftStart ();
-Window.ResizeTopLeftEnd ();

Unity useful API:
-Screen.Width : int;
-Screen.Height : int;
-Screen.currentResolution : Vector2;
and many more...

Example Uses:
Create game launchers like the ones of top branded games (example: League of Legends or Steam !!) with no or custom borders.
Create games with awesome borders that make the difference in the user's desktop !!
Simulate Window movement or teleport the window dynamically without having the user do anything !!
Create popup/warning windows that can't be closed by the user and display information or other useful stuff.
Create invisible windows by having their width or height be 0 or even below 0.
Move your windows outside the screen or in places that the cursor usually cant go.
TOP: Have fullscreen windowed mode at full resolution without waiting for Unity's standard fullscreen buffering !!!
and many more, just use your imagination and creativity...

How to install the plugin:
The first thing you may want to do is use it , but you will have to make a few steps before beeing able to do so.
1.Open Unity and go to Edit -> Project Settings -> Player -> Other Settings -> Api Compatibility Level and set it to ".NET 2.0"
2.Drag the "Window" C# script located inside of Plugins folder and drop it on any form of gameobject inside your scene.
(2.1 Warning: Do not deactivate the script. Drop it once , having multiple "Window" scripts inside one scene is not supported.)
3.Do not change the location of the System.Windows.Forms file located inside the Plugins folder.
4.If you don't already know, if you change the location of "Window" C# script you wont be able to call any of the API above from any UnityScript !
5.For more accurate installation guide read the Installation_Guide.txt

How to correctly use it:
Note : This plugin is not meant to be used in the editor , most functions are protected (cant be used in the editor) to protect you from causing damage or annoying behavior. Also some of the read or "get" values will return wrong information in the editor.
---------------------------------------------------------------
"FullyAutoBorderless"
Once you have dropped the script into a gameobject then you will see a tickbox variable that says "Fully Auto Borderless"
If you have this variable enabled and you build your standalone , it will always force the borderless mode even if you try later to set it to bordered by code it will turn back to borderless !
This is good if you want the standalone to be borderless from the beginning to the end the only drawback is that it requires the client to restart once every time you decide to remove or add borders.
We can counter this behaviour by having a launcher scene (that is already included as an example).
---------------------------------------------------------------
"QuickAutoBorderless"
Once you have dropped the script into a gameobject then you will see a tickbox variable that says "Quick Auto Borderless"
This is a lightweight borderless mode that will not require a restart to activate with the drawnback that it can't be resized without making the borderss return while resizing..
If your project requires little to no window resizing or you are ok with your client to make this behaviour then this option is for you.
---------------------------------------------------------------
"AllowSizeResettingBeforeExit"
Once you have dropped the script into a gameobject then you will see a tickbox variable that says "Allow Size Resetting Before Exit"
This variable is create to make the window launch with a small size (116 x 90) and reduce problems while increasing performance.
It also recommended to edit player setting and set this resolution to the window.
(Read installation guide for more info)
---------------------------------------------------------------
-Window.Minimize ();
Minimizes the window as if you had clicked the minimize button.
---------------------------------------------------------------
-Window.Fullscreen ();
Sets the window into fullscreen mode , by preserving the current resolution.
(Works just like standard Unity fullscreen function)
---------------------------------------------------------------
-Window.Fullscreen (Vector2);
Same as above but allows you to set the quality as a Vector2
---------------------------------------------------------------
-Window.Fullscreen (int,int);
Same as above but allows you to set the quality as two integers instead of one Vector2
---------------------------------------------------------------
-Window.Maximize ();
Will Maximize the window at full speed and have the same results as if you had clicked the maximize button,
if you are running on borderless mode it will look like fullscreen mode but a lot faster :)
---------------------------------------------------------------
-Window.Maximize (boolean);
Same as above but if the boolean is set to false it will take into consideration the min/max width/height you have set and will result into a max size mode ;)
---------------------------------------------------------------
-Window.Exit ();
Similar to Unity's default quit function.
---------------------------------------------------------------
-Window.ForceExit ();
Will force window exit with a high priority and will cause the window to close very fast even if its not responding !! :)
---------------------------------------------------------------
-Window.IsDoneLoading();
Returns true if all calculatings of the window plugin are done at least once. Good for getting accurate info in the Start or Awake function.
---------------------------------------------------------------
-Window.IsBorderless ();
Returns true if the standalone is borderless and false if the window is bordered.
---------------------------------------------------------------
-Window.Border ();
Will make the window borderless if it has borders or will make the window bordered if its borderless.
(Warning , it will force the window to restart and thus lose all processes so be careful to save before calling that)
---------------------------------------------------------------
-Window.Border (boolean);
Will set borderless mode if set to false,
will set to bordered mode if set to true.
(Same warning as above)
---------------------------------------------------------------
-Window.SetMinWidth (int);
Will set the minimum width allowed to the window
(Some functions have the ability to ignore this)
---------------------------------------------------------------
-Window.SetMaxWidth (int);
Will set the maximum width allowed to the window
(Some functions have the ability to ignore this)
---------------------------------------------------------------
-Window.SetMinHeight (int);
Will set the minimum height allowed to the window
(Some functions have the ability to ignore this)
---------------------------------------------------------------
-Window.SetMaxHeight (int);
Will set the maximum height allowed to the window
(Some functions have the ability to ignore this)
---------------------------------------------------------------
-Window.GetMinWidth (int);
Returns the minimum width you have set through Window.SetMinWidth as an int
---------------------------------------------------------------
-Window.GetMaxWidth (int);
Returns the maximum width you have set through Window.SetMaxWidth as an int
---------------------------------------------------------------
-Window.GetMinHeight (int);
Returns the minimum height you have set through Window.SetMinHeight as an int
---------------------------------------------------------------
-Window.GetMaxHeight (int);
Returns the maximum height you have set through Window.SetMaxHeight as an int
---------------------------------------------------------------
-Window.SetRect (Rect);
Sets the window's position and size at the same time to the given Rect
---------------------------------------------------------------
-Window.SetRect (int,int,int,int);
Same as above but instead of a rect it gets 4 separate ints
---------------------------------------------------------------
-Window.SetPosition (int,int);
Sets the position of the window only to the given int values and preserves the width and height
(Height and Width will not work right in bordered mode)
---------------------------------------------------------------
-Window.SetPosition (Vector2);
Same as above but instead of two ints it takes one Vector2
---------------------------------------------------------------
-Window.SetSize (int,int);
Sets the window's Size to the given int values and preserves the location
---------------------------------------------------------------
-Window.SetSize (Vector2);
Same as above but instead of two ints it takes one Vector2
---------------------------------------------------------------
-Window.GetBorderRect ();
Returns the Border Position and Overall Size as a Rect
---------------------------------------------------------------
-Window.GetBorderPosition ();
Returns the Border Position as a Vector2
---------------------------------------------------------------
-Window.GetBorderSize ();
Returns the Border Width and Height as a Vector2
(Might not work right)
---------------------------------------------------------------
-Window.GetRect ();
Returns the Window Position and Overall Size as a Rect
---------------------------------------------------------------
-Window.GetPosition ();
Returns the Window Position as a Vector2
---------------------------------------------------------------
-Window.GetSize ();
Returns the Window Size as a Vector2
---------------------------------------------------------------
-Window.GrabStart ();
Once called the Window will be "grabbed" by the cursor and will keep following it while preserving its offset until Window.GrabEnd is called
(Always make sure you are able to call GrabEnd because once GrabStart is activated , you wont be able to click anywhere)
(Grab and other Resize functions cant be active at the same time)
---------------------------------------------------------------
-Window.GrabEnd ();
Will give an end to the active GrabStart function
---------------------------------------------------------------
-Window.QuickDisableBorders();
This function will disable the borders of the window using the latest "Quick" method.
You will probably not need to call it but so far it is only used in Unity5 after entering and exiting fullscreen mode to re-deactivate the borders if using QuickBorderless option.
---------------------------------------------------------------
-Window.ResizeLeftStart ();
Once called it will start changing the size of the window only from the left side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeLeftEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeLeftEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeDownLeftStart ();
Once called it will start changing the size of the window only from the left and down side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeDownLeftEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeDownLeftEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeDownStart ();
Once called it will start changing the size of the window only from the down side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeDownEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeDownEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeDownRightStart ();
Once called it will start changing the size of the window only from the down and right side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeDownRightEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeDownRightEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeRightStart ();
Once called it will start changing the size of the window only from the right side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeRightEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeRightEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeRightTopStart ();
Once called it will start changing the size of the window only from the right and top side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeRightTopEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeRightTopEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeTopStart ();
Once called it will start changing the size of the window only from the top side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeTopEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeTopEnd ();
Will stop resizing the window if the above function was called.
---------------------------------------------------------------
-Window.ResizeTopLeftStart ();
Once called it will start changing the size of the window only from the top and left side according to the mouse position while preserving its offset.
(Its important to call the Window.ResizeTopLeftEnd function or else you wont be able to call any other grab or resize functions or click anywhere)
---------------------------------------------------------------
-Window.ResizeTopLeftEnd ();
Will stop resizing the window if the above function was called.
--------------------------------------------------------------
--------------------------------------------------------------
UNITY 5+ NOTES :
If using Unity 5.x the QuickBorderless Option will work way better , the only drawnback noticed is that after entering and exiting fullscreen mode, you will most likely need to call "QuickDisableBorders(); function after exiting fullscreen mode.
Therefore the Launcher Scene is not needed..

Conctact Info :
Email : DarknessBlade.Original@gmail.com
Youtube : https://www.youtube.com/DarknessBladeOrigin (We have TUTORIAL VIDEOS)

ASSET STORE 
VIDEO-1
VIDEO-2




[출처] https://forum.unity3d.com/threads/borderless-standalone-window-resizable-movable.298229/

반응형
Posted by blueasa
, |

[추가]

Landscape은 아래 2개 변수를 제어


Screen.autorotateToLandscapeLeft

Screen.autorotateToLandscapeRight





Disable screen auto-rotation in a game in Unity. 

Option 1


Create a script that disables auto rotate to portrait and sets the orientation to landscape.

using UnityEngine;

public class CameraScript : MonoBehaviour {

    void Start() {
        DisableAutoRotation ();
    }

    public void DisableAutoRotation() {
        Screen.autorotateToPortrait = false;
        Screen.autorotateToPortraitUpsideDown = false;
        Screen.orientation = ScreenOrientation.Landscape;
    }
}



Create a game object to attach the script to. This can be any game object. You could also attach the script to the main camera in the scene either. When the game is run on hand held devices, the autorotation will be disabled.

Option 2


Another solution to this problem is to change this setting when building the project. 
Go to 
File -> Build Settings...




Click on Player Settings...


Click on Resolution and Presentations panel in the inspector. This will then expand and present more options.




In the Resolution and Presentation panel there are options for configuring the orientation.
First select the default Orientation. This is the orientation that the game will start in. 
You can configure what orientations are allowed when the game is being run. To do this, check or uncheck the required boxes for "Allowed Orientations for Auto Rotation".




[출처] http://andrewstutorialblog.blogspot.kr/2014/12/disable-screen-auto-rotation-on-unity3d.html

반응형
Posted by blueasa
, |

동영상을 한 번 보면 설명 끝.

 

단 엑셀은 2003버전으로 컨버팅 하는 것이 조금 아쉽다.

2010쓰다가 2003 포맷하니 에러 겁나 난다. ㅠㅠ

 

컬럼명은 반드시 적혀있어야 한다.

ex)

sq    id

1      2

 

       id

1     2

 

이와 같이 윗부분에는 sq라고 적혀있고, 하단에는 없을 경우...

하단 부분은 에러난다. 최상위 부분은 비워두면 안 된다.

 

다운로드

http://exceldatareader.codeplex.com/ 

http://terasur.blog.fc2.com/blog-entry-511.html  -> 수정된 버전.

NPOI에러날 경우. 다운받는 곳.

https://npoi.codeplex.com/releases 

 

사용방법

http://www.youtube.com/watch?v=fHAHKBIqgM4&noredirect=1 




[출처] http://blog.naver.com/clater11/220074485690

반응형
Posted by blueasa
, |


[링크] https://bitbucket.org/Unity-Technologies/



[참조] https://youtu.be/iRZoYI1NUmM

반응형
Posted by blueasa
, |

[링크] https://github.com/pumperer/InfiniteList_NGUI



Description

A component that runs on top of NGUI's UIScrollView & UITable classes for Unity3D (i.e. It requires NGUI & Unity3D) can be used with dynamic data.

Instead of instantiating a Prefab for each row in the list we are instantiating a fixed pool of objects that will be reused according to the scroll direction.

Best suited for Mobile (tested on both iOS & Android).

Features

  • Infinite scrolling with clipping panel
  • Only fixed height cells are supported for now
  • Basic Scroll indicator
  • Quick jump (selecting a start point)
  • Simple sections implementation (section height need to be equal to the item height)
  • Click item callbacks
  • Tested on Unity v4.2.2 & Unity v4.3 and NGUI v3.0.9f7

The Demo

This demo package requires both Unity3D http://unity3d.com and NGUI http://www.tasharen.com/?page_id=140 to be installed. video: https://www.youtube.com/watch?v=5xFVJqzp0kY

To run the demo:

  1. Create a new Unity Project (preferably mobile i.e. iOS or Android)
  2. Import NGUI including its examples as the demo uses the atlas's from the examples
  3. Import InfiniteListDemo folder or simply double click on InfiniteListDemoPackage
  4. Run the scene (InfiniteListDemo)

The demo package is released under the MIT License: http://opensource.org/licenses/MIT

Example of app using this component is Avatar Messenger for Android (Contacts list view) which is a free app on Google Play: https://play.google.com/store/apps/details?id=com.orange.labs.avachat

Main Classes & Methods in the Demo

InfiniteListPopulator

The main controller script that can be attached to a gameobject (e.g. the panel)

Some of the main methods included:

Initializes the list (also can be called to refresh the list with new data)

public void InitTableView(ArrayList inDataList, List<int> inSectionsIndices, int inStartIndex)

Parameters:

  • inDataList: the generic list of our data (you can change it to any type you want… just make sure to change the member variables types for dataList & OriginalData)* inNumberOfSections: number of sections (int)
  • inSectionIndices: List of integers. The start index of each section (not as fancy as indexpath in iOS but did the job for me)
  • inStartIndex: where to start

Refresh the list without changing the data (list start at startIndex value)

public void RefreshTableView()

Individual methods for changing the parameters if needed

public void SetStartIndex(int inStartIndex)
public void SetOriginalData(ArrayList inDataList)
public void SetSectionIndices(List<int> inSectionsIndices)

You can include section titles values.. or if you have more detailed sections seperators you can change the implementation of PopulateListSectionWithIndex

string GetTitleForSection(int i)
void PopulateListSectionWithIndex(Transform item, int index)

You can do your implementation of what to populate your row item with (in the demo we simply set a label to string value from our datalist array). Simply change InfiniteItemBehaviour (mentioned later below) to include more items as you want.

void PopulateListItemWithIndex(Transform item, int dataIndex)

Events that can be listened to.

public event InfiniteItemIsPressed InfiniteItemIsPressedEvent;
public event InfiniteItemIsClicked InfiniteItemIsClickedEvent;

InfiniteItemBehaviour and InfiniteSectionBehaviour

Scripts attached to the row item prefab & section prefab (Note: the item prefab need to be tagged as "listItem" and the section prefab as "listSection")

Both checks for the visiblity of the item when the list start scrolling and notifiy the InfiniteListPopulator when the items becomes invisible. you can change use them as a template and include more labels, sprites or textures.

반응형

'Unity3D > NGUI' 카테고리의 다른 글

[펌] Blur filter for UITexture in NGUI  (0) 2017.02.09
[펌] NGUI 쉽게 말풍선 만들기  (0) 2017.02.03
NGUI CoverFlow(with Reflection Shader)  (0) 2016.10.11
[Link] Unity-NGUIExtension  (0) 2016.10.10
[펌] Smart Localization with NGUI  (0) 2016.09.26
Posted by blueasa
, |

Parallax scrolling

For the moment, we have created a static scene with a player and some enemies. It’s a bit boring. Time to enhance our background and scene.

An effect that you find in every single 2D game for 15 years is “parallax scrolling”.

To make it short, the idea is to move the background layers at different speeds (i.e., the farther the layer is, the slower it moves). If done correctly, this gives an illusion of depth. It’s a cool, nice and easy-to-do effect.

Moreover, many shmups use a scrolling in one — or more — axis (except the original one, Space Invaders).

Let’s implement that in Unity.

Theory: defining the scrolling in our game

Adding a scrolling axis need a bit of thinking on how we will make the game with this new aspect.

It’s good to think before coding. :)

What do we want to move?

We have a decision to take here :

  1. First choice: The player and the camera move. The rest is fixed.
  2. Second choice: The player and the camera are static. The level is a treadmill.

The first choice is a no-brainer if you have a Perspective camera. The parallax is obvious: background elements have a higher depth. Thus, they are behind and seems to move slower.

But in a standard 2D game in Unity, we use an Orthographic camera. We don’t have depth at render.

About the camera: remember the “Projection” property of your camera game object. It’s set to Orthographic in our game.

Perspective means that the camera is a classic 3D camera, with depth management. Orthographic is a camera that renders everything at the same depth. It’s particularly useful for a GUI or a 2D game.

In order to add the parallax scrolling effect to our game, the solution is to mix both choices. We will have two scrollings:

  • The player is moving forward along with the camera.
  • Background elements are moving at different speeds (in addition to the camera movement).

Note: you may ask: “Why don’t we just set the camera as a child of the player object?”. Indeed, in Unity, if you set an object (camera or not) as a sub-child of a game object, this object will maintain its relative position to its parent. So if the camera is a child of the player and is centered on him, it will stay that way and will follow him exactly. It could be a solution, but this would not fit with our gameplay.

In a shmup, the camera restricts the player movement. If the camera moves along with the player for both horizontal and vertical axis, then the player is free to go where he wants. We DO want to keep the player inside a restricted area.

We would also recommend to always keep the camera independent in a 2D game. Even in a platformer, the camera isn’t strictly linked to the player: it follows him under some restrictions. Super Mario World has probably one the best camera possible for a platformer. You may have a look at how it is done.

Spawning enemies

Adding a scrolling to our game has consequences, especially concerning enemies. Currently, they are just moving and shooting as soon as the game starts. However, we want them to wait and be invincible until they spawn.

How do we spawn enemies? It depends on the game, definitely. You could define events that spawn enemies when they are triggered, spawn points, pre-determined positions, etc.

Here is what we will do: We position the Poulpies on the scene directly (by dragging the Prefab onto the scene). By default, they are static and invincibles until the camera reaches and activates them.

Camera usage

The nice idea here is that you can use the Unity editor to set the enemies. You read right: without doing anything, you already have a level editor.

Once again, it’s a choice, not science. ;)

Note: on a bigger project, you may need a dedicated level editor such as “Tiled” or a custom one you made. Your levels can be text files (plain text, XML, JSON, etc.) that you read in Unity for example.

Planes

First, we must define what our planes are and for each, if it’s a loop or not. A looping background will repeat over and over again during the level execution. E.g., it’s particularly useful for things like the sky.

Add a new layer to the scene for the background elements.

We are going to have:

Layer  Loop  
Background with the skyYes
Background (1st row of flying platforms)No
Middleground (2nd row of flying platforms)No
Foreground with players and enemiesNo

Planes

We could add as many layers of background objects as we want.

Careful with that axe, Eugene: if you add layers ahead of the foreground layer, be careful with the visibility. Many games do not use this technique because it reduces the clearness of the game, especially in a shmup where the gameplay elements need to be clearly visible.

Practice: Diving into the code

Okay, we saw how implementing a parallax scrolling affects our game.

Did you know? “Scrolling shooters” is another name used for the shmups.

But enough thoughts, time to practice!

Unity has some parallax scrolling scripts in its standard packages (take a look at the 2D platformer demo on the Asset Store). You can of course use them, but we found it would be interesting to build one from scratch the first time.

Standard packages: these are practicals, but be careful to not abuse of them. Using standard packages can block your thoughts and will not make your game stand out of the crowd. They give a Unity feel to your gameplay.

Remember all the flash game clones?

Simple scrolling

We will start with the easy part: scrolling backgrounds without looping.

Remember the “MoveScript” we used before? The basis is the same: a speed and a direction applied over time.

Create a new “ScrollingScript” script:

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,
      0);

    movement *= Time.deltaTime;
    transform.Translate(movement);

    // Move the camera
    if (isLinkedToCamera)
    {
      Camera.main.transform.Translate(movement);
    }
  }
}

Attach the script to these game objects with these values:

LayerSpeedDirectionLinked to Camera
Background(1, 1)(-1, 0, 0)No
Background elements(1.5, 1.5)(-1, 0, 0)No
Middleground(2.5, 2.5)(-1, 0, 0)No
Foreground(1, 1)(1, 0, 0)Yes

For a convincing result, add elements to the scene:

  • Add a third background part after the two previous ones.
  • Add some small platforms in the layer ` Background elements`.
  • Add platforms in the layer Middleground.
  • Add enemies on the right of the layer Foreground, far from the camera.

The result:

Scrolling effect

Not bad! But we can see that enemies move and shoot when they are out of the camera, even before they spawn!

Moreover, they are never recycled when they pass the player (zoom out in the “Scene” view, and look at the left of the scene: the Poulpies are still moving).

Note: experiment with the values. :)

We’ll fix these problems later. First, we need to manage the infinite background (the sky).

Infinite background scrolling

In order to get an infinite background, we only need to watch the child which is at the left of the infinite layer.

When this object goes beyond the camera left edge, we move it to the right of the layer. Indefinitely.

Infinite scrolling theory

For a layer filled with images, notice that you need a minimum size to cover the camera field, so we never see what’s behind. Here it’s 3 parts for the sky, but it’s completely arbitrary.

Find the correct balance between resource consumption and flexibility for your game.

In our case, the idea is that we will get all the children on the layer and check their renderer.

A note about using the renderer component: This method won’t work with invisible objects (e.g., the ones handling scripts). However, a use case when you need to do this on invisible objects is unlikely.

We will use an handy method to check whether an object’s renderer is visible by the camera. We’ve found it on the community wiki. It’s neither a class nor a script, but a C# class extension.

Extension: the C# language allows you to extend a class with extensions, without needing the base source code of the class.

Create a static method starting with a first parameter which looks like this: this Type currentInstance. The Type class will now have a new method available everywhere your own class is available.

Inside the extension method, you can refer to the current instance calling the method by using the currentInstance parameter instead of this.

The “RendererExtensions” script

Create a new C# file named “RendererExtensions.cs” and fill it with:

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

Simple, isn’t it?

Namespaces: you might have already noted that Unity doesn’t add a namespace around a MonoBehaviour script when you create it from the “Project” view. And yet Unity does handle namespaces…

In this tutorial, we are not using namespaces at all. However, in your real project, you might consider to use them. If not, prefix your classes and behaviors to avoid a collision with a third-party library (like NGUI).

The real reason behind not using namespaces was that during the Unity 4 days (this tutorial was originally written for Unity 4.3), a namespace would prevent the use of default parameters. It’s not a problem anymore, so: use namespace!

We will call this method on the leftmost object of the infinite layer.

Full “ScrollingScript”

Observe the full “ScrollingScript” (explanations below):

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>
    /// 1 - Background is infinite
    /// </summary>
    public bool isLooping = false;

    /// <summary>
    /// 2 - List of children with a renderer.
    /// </summary>
    private List<SpriteRenderer> backgroundPart;

    // 3 - 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,
          0);

        movement *= Time.deltaTime;
        transform.Translate(movement);

        // Move the camera
        if (isLinkedToCamera)
        {
            Camera.main.transform.Translate(movement);
        }

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

(The numbers in the comments refer to the explanations below)

Explanations

  1. We need a public variable to turn on the “looping” mode in the “Inspector” view.
  2. We also have to use a private variable to store the layer children.
  3. In the Start() method, we set the backgroundPart list with the children that have a renderer. Thanks to a bit of LINQ, we order them by their X position and put the leftmost at the first position of the array.
  4. In the Update() method, if the isLooping flag is set to true, we retrieve the first child stored in the backgroundPart list. We test if it’s completely outside the camera field. When it’s the case, we change its position to be after the last (rightmost) child. Finally, we put it at the last position of backgroundPartlist.

Indeed, the backgroundPart is the exact representation of what is happening in the scene.


Remember to enable the “Is Looping” property of the “ScrollingScript” for the 0 - Background in the “Inspector” pane. Otherwise, it will (predictably enough) not work.

Infinite scrolling

(Click on the image to see the animation)

Yes! We finally have a functional “parallax scrolling” implementation.

Note: why don’t we use the OnBecameVisible() and OnBecameInvisible() methods? Because they are broken.

The basic idea of these methods is to execute a fragment of code when the object is rendered (or vice-versa). They work like the Start() or Stop() methods (if you need one, simply add the method in the MonoBehaviour and Unity will use it).

The problem is that these methods are also called when rendered by the “Scene” view of the Unity editor. This means that we will not get the same behavior in the Unity editor and in a build (whatever the platform is). This is dangerous and absurd. We highly recommend to avoid these methods.

Bonus: Enhancing existing scripts

Let’s update our previous scripts.

Enemy v2 with spawn

We said earlier that enemies should be disabled until they are visible by the camera.

They should also be removed once they are completely off the screen.

We need to update “EnemyScript”, so it will:

  1. Disable the movement, the collider and the auto-fire (when initialized).
  2. Check when the renderer is inside the camera sight.
  3. Activate itself.
  4. Destroy the game object when it’s outside the camera.

(The numbers refer to the comments in the code)

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

    // 1 - 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()
    {
        // 2 - 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);
                }
            }

            // 4 - Out of the camera ? Destroy the game object.
            if (rendererComponent.IsVisibleFrom(Camera.main) == false)
            {
                Destroy(gameObject);
            }
        }
    }

    // 3 - 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;
        }
    }
}

Start the game. Yes, there’s a bug.

Disabling the “MoveScript” as a negative effect: The player never reaches the enemies as they’re all moving with the Foreground layer scrolling:

camera_moving_along_gif

Remember: we’ve added a “ScrollingScript” to this layer in order to move the camera along with the player.

But there is a simple solution: move the “ScrollingScript” from the Foregroundlayer to the player!

Why not after all? The only thing that is moving in this layer is him, and the script is not specific to a kind of object.

Push the “Play” button and observe: It works.

  1. Enemies are disabled until they spawn (i.e., until the camera reaches their positions).
  2. Then they disappear when they are outside the camera.

Enemy spawn

(Click on the image to see what happens)

Keeping the player in the camera bounds

You might have noticed that the player is not (yet) restricted to the camera area. “Play” the game, push the “Left Arrow” and watch him leaves the camera.

We have to fix that.

Open the “PlayerScript”, and add this at the end of the “Update()” method:

  void Update()
  {
    // ...

    // 6 - 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
  }

Nothing complicated, just verbose.

We get the camera edges and we make sure the player position (the center of the sprite) is inside the area borders.

Tweak the code to better understand what is happening.

Next step

We have a scrolling shooter!

We have just learned how to add a scrolling mechanism to our game, as well as a parallax effect for the background layers. However, the current code only works for right to left scrolling. But with your new knowledge, you should be able to enhance it and make it work for all scrolling directions (bonus: We did it as someone was stuck on the subject, click to see the code and an animation).

Still, the game really needs some tweaks to be playable. For example:

  • Reducing the sprite sizes.
  • Adjusting the speeds.
  • Adding more enemies.
  • Making it fun.

We will address these points in our upcoming chapter about gameplay tweaking (not released yet, unfortunately). For the moment, you can experiment. ;)

In the next chapter, we will focus our attention on how to make the game a bit more… flashy. With particles!




[출처] http://pixelnest.io/tutorials/2d-game-unity/parallax-scrolling/

반응형
Posted by blueasa
, |

최적화 Tips

GameObject를 비활성화

필요한 경우가 아니면 GameObject를 비활성화 해라. 개체수를 감소시켜라.

Scripts를 비활성화

필요한 경우가 아니면 Scripts를 비활성화 해라.

콜백(Update, OnEnable등) 함수의 사용을 자제하자

  • 새로운 스크립트를 생성시에 Update가 생성되는데 불필요한 경우 삭제하자. 그리고 Update에서 꼭 처리해야 하는 경우가 아니면 다른 방법으로 해결하라.
  • Coroutine을 적극 활용해라. Update함수 대신에 Coroutine을 사용할 수 있는 경우는 적극 활용하자. FadeIn/Out 같은 경우 단기간 필요한 기능이므로 코루틴으로 만들 경우 일시적으로 부하가 발생하므로 Update처럼 지속적으로 부하를 일으키지 않는다.

Find류 GetComponent류 함수의 호출 횟수를 줄이자

Find, FindByTag, GetComponent류의 사용을 자제해야 한다. 특히 Update, FixedUpdate같은 곳에서 지속적으로 사용하는 것은 퍼포먼스에 악영향이 있다. Awake, OnEnable에서 미리 찾아서 가지고 있는 것이 좋다.

유니티 엔진에서 제공하는 속성에 접근시 부하 발생

Transform.position, gameObject.transform, gameObject.camera 등 유니티 엔진에서 C# 프로퍼티로 랩핑하여 제공하는 변수 타입에 접근시에 부하와 임시 객체가 생성된다. 캐싱하여 사용하는 것이 좋다.

태그 비교시에 if ( go.tag == “myBot” ) 구문 대신에 go.compareTag(“myBot”)을 사용하는 것이 임시 객체 생성을 회피하는 방법이다.

LINQ 명령어 사용 자제

LINQ 명령어를 사용하는 경우 중간 버퍼가 할당된다. 이는 가비지로 남게 된다.

박싱 언박싱 회피

꼭 필요한 경우가 아니면 SendMessage를 사용하지 말라

SendMessage류의 사용을 자제해야 한다. 직접 함수를 호출하는 경우보다 SendMessage류를 사용하는 경우 100배 느리다. 그리고 호출시에 함수 이름을 넣게 되는데 코드 난독화가 필요한 경우 장애요소가 된다.

foreach() 사용을 자제하자

foreach()를 사용할 경우 enumerator를 생성하기 위해서 힙 메모리를 할당한다. for(;;)문을 활용하는 것이 좋다. 한번 돌 때마다 24bit의 가비지를 남긴다.

문자열

  1. string을 그대로 사용해서 조합 하는 것 보다 StringBuilder를 사용하는 것이 좀더 메모리 친화적이고 퍼포먼스 업에 도움이 된다.
  2. 혹은 String.Format을 사용하자.
  3. 상수 타입 문자열은 const, readonly를 지정하여 가비지 컬렉션에서 제외시키자.



[출처] http://www.antegg.com/wiki/doku.php?id=note:unity_3d:%EC%B5%9C%EC%A0%81%ED%99%94_tips

반응형
Posted by blueasa
, |