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

카테고리

분류 전체보기 (2737)
Unity3D (817)
Programming (474)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (58)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
04-18 17:47


[링크] http://ttend.tistory.com/251

반응형
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
, |

다른 오브젝트의 함수를 써야할 때 메시지 호출방법을 쓰는 것이 좋다고 합니다.


이득우 님의 발표자료에 따르면, (아래 화면 참조)

  • 메시지 호출은 클래스 간 의존성을 줄일 수 있고,
  • 이는 코드를 간단하게 만들 수 있는데,
  • 성능의 저하가 있다고 생각하지만 그렇게 느리지 않고 장점이 더 크니 적극적으로 쓰자.
는 결론입니다.

<컴포넌트>.SendMessage 또는 <게임오브젝트>.SendMessage 와 같은 방식으로 쓸 수 있습니다.

구문 형식:
void SendMessage(string methodName, object value = null, SendMessageOptions options = SendMessageOptions.RequireReceiver); 


유니티 레퍼런스 홈페이지 예제를 보면,


using UnityEngine;

using System.Collections;


public class Example : MonoBehaviour {

    void ApplyDamage(float damage) {

        print(damage);

    }

    void Example() {

        SendMessage("ApplyDamage", 5.0F);

    }

}


위와 같은 방식으로 사용하고 있습니다. 파라미터 중 마지막 SendMessageOptions 은 말 그대로 옵션인데, SendMessageOptions.DontRequireReceiver 라고 쓸 경우에는 호출한 함수가 제대로 호출되었는지 여부는 신경쓰지 않고 호출한 것으로 역할이 끝납니다.


C# 에서는 콜백의 기능을 위해 delegate 를 사용할 수도 있습니다. 이와 관련해서 아래의 링크를 참고하세요.


http://unityindepth.tistory.com/entry/유니티-개발자로써-내가-배웠던-최고의-5가지





[출처] http://unitygame.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-SendMessage-%EC%82%AC%EC%9A%A9





[첨부]


  
빕스

덧붙이자면 
SendMessage 도 생각보다 느리지 않다고 알고있습니다.

실시간으로 리플랙션을 통해 함수를 호출하는것이 아니라
엔진에서 맵형태로 관리하기때문에
스트링 비교하는 부하정도만 있는걸로 알고있습니다.


[출처] http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talkdev&no=2070

반응형
Posted by blueasa
, |


[링크] http://m.todayhumor.co.kr/view.php?table=bestofbest&no=273074&page=3

반응형
Posted by blueasa
, |


[링크] http://m.todayhumor.co.kr/view.php?table=bestofbest&no=76831

반응형
Posted by blueasa
, |


[링크] http://catbook.kr/90

반응형
Posted by blueasa
, |

링크 : http://www.petzzi.com/bbs/board.php?bo_table=ency_info&wr_id=313

반응형
Posted by blueasa
, |

Have you ever found yourself wishing a built-in Unity class had some functionality that isn’t there? C#extension methods are the answer!

In this article, I’ll teach you how to use extension methods to add functionality to existing classes, no matter if they’re built-in Unity types, types defined in a third-party plugin, or even types defined in an Asset Store package which you could edit but you’re (rightly) worried about later package updates stomping your “patch”.

Seemingly obvious API omissions can be frustrating, but extension methods let you “fix” just about any API to your liking.

Hit the jump for all the details!

When Extension Methods Are Useful

Imagine you need a way to set the layer of a GameObject and all its children, but there’s no GameObject.SetLayerRecursively() available. You could embed a loop in your code:

// Do some work.

    // Set the layer of this GameObject and all its children.
    gameObject.layer = someLayer;
    foreach(Transform t in transform)
        t.gameObject.layer = someLayer;

    // Do some more work.

This will work fine, although it’s not the cleanest thing in the world, and you’ll need to copy these lines of code around to every place where you want to do this operation. It would be better to encapsulate that code in a function, and make that function available to everyone via a “helper” class:

public class GameObjectHelper
    {
        public static void SetLayerRecursively(GameObject gameObject, int layer)
        {
            gameObject.layer = someLayer;
            foreach(Transform t in transform)
                t.gameObject.layer = someLayer;
        }
    }

    public class Test : MonoBehaviour
    {
        void Start()
        {
            GameObjectHelper.SetLayerRecursively(gameObject, someLayer);
        }
    }

This works fine too, but it can be easy to forget to use the helper class to invoke a function that feels like it belongs in GameObject itself. Wouldn’t it be nicer if you could just do this?

gameObject.SetLayerRecursively(someLayer);

As it turns out, C# lets you make this happen!

Declaring An Extension Method

Extension methods are declared just like regular functions, except that a) they must be static, b) they must be declared inside a non-generic, non-nested, static class, and c) they include a special syntax for their first argument, which is a reference to the calling object:

public static class GameObjectExtensions
    {
        public static void SetLayerRecursively(this GameObject gameObject, int layer)
        {
            // Implementation goes here
        }
    }

Though declared as static, this is invoked as if it were an instance method:

myGameObject.SetLayerRecursively(someLayer);

Note that when we call the method we actually omit the first argument and skip straight to the second. Take another look at the method declaration above. See how the first argument is declared using the “this” keyword? That’s what tells the compiler to infer that argument as the calling object; in this case, myGameObject.

That’s actually all there is to it. Extension methods are easy!

For what it’s worth, I like to organize my extension methods into classes named ClassNameExtensions. So I have GameObjectExtensions, TransformExtensions, ColorExtensions, and so on. There’s nothing that says you have to do this; I just like the organization. You could pack them all together into a single Extensions class if you prefer, or any other kind of organization you want. Just remember that extensions methods must be declared inside a non-generic, non-nested, static class; the compiler will complain otherwise.

Limitations of Extension Methods

Extension methods can add to an existing class, but they cannot override existing methods. For example, if you declared this:

public static class GameObjectExtensions
    {
        public static Component GetComponent(this GameObject gameObject, Type type)
        {
            Debug.LogError(LOL, you got trolled);
        }
    }

…the compiler would basically ignore you. The rule for multiple method declarations using the same signature is: instance methods always take precedence over extension methods.

To extend a type, you’ll need make sure it’s in scope with the “using” directive. If you’re extending any built-in Unity type, you’re covered by virtue of the “using UnityEngine” that’s a standard entry in most Unity scripts. If you’re extending an editor type, you’ll need to add “using UnityEditor”, just like you would if you were calling that type. If you’re extending a type from a third-party plugin, you may need to import a namespace; check the plugin’s documentation (or source code, if you have it) for details.

I’ve already mentioned that extension methods must be declared inside a non-generic, non-nested, static class, which means you can’t just drop them in willy-nilly wherever you want. In practice, this “limitation” turns out to be a useful organizing device. You can certainly argue that this is virtually identical to the “helper class” example at the top of this article, and in terms of implementation effort that’s probably true; the difference is that with extension methods you get a cleaner, more normalized calling syntax. This really comes down to personal preference.

Some Useful Extension Methods

For the rest of this article, I’ll share some useful extension methods I’ve written over the past few months. Feel free to incorporate these into your own codebase and modify them at-will. (But don’t just copy-paste them into a text file and try to sell it on the Asset Store; that would make you a horrible person.)

Set Layer Recursively

I use the excellent 2D Toolkit for UI, and I use a two-camera setup: one camera for the scene, one camera for the UI. In order to make the UI camera render only UI objects, all the UI objects need to be on a UI layer. When I create UI objects from script I need to set that layer, and often I’m creating objects with children (like a button with a child sprite and a child text label).

It’d be nice to have a way to set the layer for this entire hierarchy with a single function call (you’ll recognize this as the example at the top of this article). Here’s what the call looks like:

myButton.gameObject.SetLayerRecursively(LayerMask.NameToLayer(UI));

And here’s that extension method:

// Set the layer of this GameObject and all of its children.
    public static void SetLayerRecursively(this GameObject gameObject, int layer)
    {
        gameObject.layer = layer;
        foreach(Transform t in gameObject.transform)
            t.gameObject.SetLayerRecursively(layer);
    }

Set Visual/Collision Recursively

While we’re doing things recursively, wouldn’t it be nice if we could enable/disable just the renderers, or just the colliders, for an entire hierarchy? This can be useful for UI, but it can also be useful in the scene. Imagine a complex hierarchy that defines a really fancy animated force field, and a game mechanic whereby you can switch your avatar’s polarity, allowing passage through force fields of a particular color. When you switch polarity, you’d loop through the matching force fields and disable their colliders, like this:

foreach(ForceField forceField in forceFields)
        forceField.gameObject.SetCollisionRecursively(false);

Here’s that extension method:

public static void SetCollisionRecursively(this GameObject gameObject, bool tf)
    {
        Collider[] colliders = gameObject.GetComponentsInChildren<Collider>();
        foreach(Collider collider in colliders)
            collider.enabled = tf;
    }

And the same principle applies for renderers:

public static void SetVisualRecursively(this GameObject gameObject, bool tf)
    {
        Renderer[] renderers = gameObject.GetComponentsInChildren<Renderer>();
        foreach(Renderer renderer in renderers)
            renderer.enabled = tf;
    }

Filter Child Components By Tag

It’s easy to search for child components using GameObject.GetComponentsInChildren(), but what if you have a have hierarchy in which you have lots of instances of a type, and you only want those instances which have a particular tag? In my case, I had a compound object with numerous renderers, and I needed to drive a material color on a subset of those renderers tagged as “Shield”.

This proved convenient:

m_renderers = gameObject.GetComponentsInChildrenWithTag<Renderer>("Shield");

Here’s that extension method:

public static T[] GetComponentsInChildrenWithTag<T>(this GameObject gameObject, string tag)
        where T: Component
    {
        List<T> results = new List<T>();

        if(gameObject.CompareTag(tag))
            results.Add(gameObject.GetComponent<T>());

        foreach(Transform t in gameObject.transform)
            results.AddRange(t.gameObject.GetComponentsInChildrenWithTag<T>(tag));

        return results.ToArray();
    }

I noted earlier that extension methods must be declared inside non-generic classes. That doesn’t mean the extension method itself can’t be generic, however! When we call this method we replace T with the type we’re interested in — in the preceding call example, it was Renderer — and that type is used for each occurrence of T in the method implementation. The “where” keyword specifies that T must be of type Component or a type derived from Component (the compiler will throw an error otherwise).

See this MSDN article for more information about generics.

Get Components In Parents

It’s all well and good to get components in children, but sometimes you need to search up. A common case I run into is when figuring out what to do with a collision result. I have a Player type, and its hierarchy is made up of several GameObjects which represent visuals, colliders, equipped items, and so on. Typically when I get a collision result on a player, the collider is bound to a child GameObject, so I can’t just do GetComponent() and then player.TakeDamage() or whatever, because there’s no Player component on the GameObject I actually hit. In this case I need to search up the hierarchy and find the Player to which this collider is parented; also the Player may not necessarily be this collider’s immediate parent.

So now I do this:

Player player = collider.gameObject.GetComponentInParents<Player>();

Here’s that extension method:

public static T GetComponentInParents<T>(this GameObject gameObject)
        where T : Component
    {
        for(Transform t = gameObject.transform; t != null; t = t.parent)
        {
            T result = t.GetComponent<T>();
            if(result != null)
                return result;
        }

        return null;
    }

Just like Unity has both GameObject.GetComponentInChildren (singular) and GameObject.GetComponentsInChildren (plural), I also created a version that gets all components in parents:

public static T[] GetComponentsInParents<T>(this GameObject gameObject)
        where T: Component
    {
        List<T> results = new List<T>();
        for(Transform t = gameObject.transform; t != null; t = t.parent)
        {
            T result = t.GetComponent<T>();
            if(result != null)
                results.Add(result);
        }

        return results.ToArray();
    }

Note: it would be trivial to create a GetComponentsInParentsWithTag, but I haven’t run into a need for it yet. If you’d like to exercise your newfound knowledge of extension methods, this might be a good exercise. :)

Get An Object’s Collision Mask

Here’s one whose omission is particularly perplexing. You can get the layer a GameObject is on, which is useful for both rendering and collision purposes, but there’s no easy way to figure out the set of layers that GameObject can collide against.

I ran into this when implementing weapons. Projectile-based weapons are simple: they get a Rigidbody and a Collider of some sort, and the collision system handles everything for me. But a rail gun-like weapon is a different story: there’s no Rigidbody, just a script-invoked raytest. You can pass a collision mask — a bitfield — into a raytest, but what if you want the collision mask to be based on the weapon’s layer? It’d be nice to set some weapons to “Team1” and others to “Team2”, perhaps, and also to ensure your code doesn’t break if you change the collision matrix in the project’s Physics Settings.

Really, I wanted to just do this:

if(Physics.Raycast(startPosition, direction, out hitInfo, distance,
        weapon.gameObject.GetCollisionMask())
    )
    {
        // Handle a hit
    }

That raycast will only hit objects which the calling weapon is allowed to collide with, based on its layer and the project’s collision matrix.

Here’s that extension method:

public static int GetCollisionMask(this GameObject gameObject, int layer = -1)
    {
        if(layer == -1)
            layer = gameObject.layer;

        int mask = 0;
        for(int i = 0; i < 32; i++)
            mask |= (Physics.GetIgnoreLayerCollision(layer, i) ? 0 : 1) << i;

        return mask;
    }

Note the optional “layer” argument. If omitted, it uses the layer of the calling GameObject, which is the most common/intuitive case (for me, at least). But you can specify a layer and it’ll hand you the collision mask for that layer instead.

Easily Change A Color’s Alpha

I often find myself wanting to modulate the alpha value of a color without changing the color itself, for blinking/pulsing effects. Because Color is a struct, and structs in C# are immutable, you can’t simply assign color.a; you’ll get a compiler error. Fortunately, extension methods can extend structs as well as classes:

public static Color WithAlpha(this Color color, float alpha)
    {
        return new Color(color.r, color.g, color.b, alpha);
    }

This method makes modulating a color’s alpha clean and simple:

GUI.color = desiredColor.WithAlpha(currentAlpha);

Performance Considerations

There really aren’t any. Extension methods are a compile-time device; once you reach runtime, they look and act just like any other method call. So it doesn’t matter whether you write an extension method or an explicit helper class; as long as the guts of the method are identical, both implementations should perform identically. You could theoretically squeeze a vanishingly tiny bit of performance out of simply inlining all the code, avoiding the overhead of any function call at all, but in this day and age that’s completely pointless to worry about unless you’re calling the function millions of times.

Extension methods are mainly useful as a way of cleaning up and normalizing your code. They have little or no effect on the behavior of that code.

Conclusion

I hope this article has been clear and useful. Extension methods are one of my favorite features of C#, because I’m a bit obsessive about having clean, easy-to-read code and they can really help make that happen. Of course, there’s more than one way to skin a cat, and I’m by no means suggesting that extension methods are the right or only way to do things. You can certainly argue that making an explicit helper class containing regular static functions — like GameObjectHelper.SetActiveRecursively() in the example at the top of this article — is just as good as an extension method-based implementation of the same; they’re six of one, half-dozen of another. I prefer the extension method approach because it feels like a natural and intuitive API extension, rather than a “bolt-on”, but that’s strictly a matter of personal preference.

Like every coding practice, extension methods are just one tool in the toolbox. But I encourage you to give them a shot!

(Oh and by the way: I'm available for contract work doing the sort of things you just read about. Hit me up if you're interested!)



[출처] http://www.third-helix.com/2013/09/30/adding-to-unitys-builtin-classes-using-extension-methods.html

반응형

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

[펌] Find objects with DontDestroyOnLoad  (0) 2021.11.26
[Unity] Play Streaming Music From Server  (0) 2018.02.06
일괄적으로 Texture Import Setting 변경  (0) 2015.01.30
Extension Methods  (0) 2014.08.18
Nullable Types  (0) 2014.08.18
Posted by blueasa
, |