Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Having trouble filling frustum with sprites

Discussion in 'Scripting' started by Sendatsu_Yoshimitsu, Sep 23, 2016.

  1. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    I'm trying to write a function that identifies the four corners of the view frustum (in 2d mode), and use those to instantiate a tile every unity unit within the frustum, effectively populating the entire visible play area with tiles:
    Code (csharp):
    1.  
    2.   Vector3 upperLeftScreen = new Vector3(0, Screen.height, Camera.main.depth);
    3.   Vector3 upperRightScreen = new Vector3(Screen.width, Camera.main.depth);
    4.   Vector3 lowerLeftScreen = new Vector3(0, 0, Camera.main.depth);
    5.   Vector3 lowerRightScreen = new Vector3(Screen.width, 0, Camera.main.depth);
    6.  
    7.   //Corner locations in world coordinates
    8.   Vector3 upperLeft = Camera.main.ScreenToWorldPoint(upperLeftScreen);
    9.   Vector3 upperRight = Camera.main.ScreenToWorldPoint(upperRightScreen);
    10.   Vector3 lowerLeft = Camera.main.ScreenToWorldPoint(lowerLeftScreen);
    11.   Vector3 lowerRight = Camera.main.ScreenToWorldPoint(lowerRightScreen);
    12.  
    13.   for (int i = (int)upperLeft.x; i < (int)lowerRight.x; i++)
    14.   {
    15.         for (int j = (int)upperLeft.y; i < (int)lowerRight.y; j++)
    16.         {
    17.               Instantiate(TileToDraw, new Vector3(i,j,0), Quaternion.identity);
    18.         }
    19.   }
    However, instead of spawning tiles in a rectangle that matches the view frustum, this draws them straight up in a vertical line. Is there something obviously weird with my math? The only other thing I can think of is that ScreenToWorldPoint might not be returning the frustum's corners like I think it is.
     
  2. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    To this code work correctly need squared sprites and set pixels per unit equal to texture width or height. Also you need orthographic camera.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Test : MonoBehaviour {
    5.  
    6.     public GameObject TileToDraw;  
    7.     public int horizontalTiles = 10;
    8.     public int verticalTiles = 10;
    9.  
    10.     void Start()
    11.     {
    12.         Camera.main.transform.position = new Vector3(0f, 0f, -5f);
    13.         Camera.main.transform.rotation = Quaternion.identity;
    14.         Camera.main.orthographic = true;
    15.  
    16.         Vector3 upperLeftScreen = new Vector3(0, Screen.height, 0);
    17.         Vector3 upperRightScreen = new Vector3(Screen.width, 0);
    18.         Vector3 lowerLeftScreen = new Vector3(0f, 0f, 0f);
    19.         Vector3 lowerRightScreen = new Vector3(Screen.width, 0, 0);
    20.  
    21.         //Corner locations in world coordinates
    22.         Vector3 upperLeft = Camera.main.ScreenToWorldPoint(upperLeftScreen);
    23.         Vector3 upperRight = Camera.main.ScreenToWorldPoint(upperRightScreen);
    24.         Vector3 lowerLeft = Camera.main.ScreenToWorldPoint(lowerLeftScreen);
    25.         Vector3 lowerRight = Camera.main.ScreenToWorldPoint(lowerRightScreen);
    26.  
    27.         float widthScreen = Mathf.Abs(upperLeft.x - upperRight.x);
    28.         float heightScreen = Mathf.Abs(upperLeft.y - lowerLeft.y);
    29.         float widthSprite = widthScreen / (float)this.horizontalTiles;
    30.         float heightSprite = heightScreen / (float)this.verticalTiles;
    31.         float cornerX = lowerLeft.x + widthSprite * 0.5f;
    32.         float cornerY = lowerLeft.y + heightSprite * 0.5f;
    33.  
    34.         //Require squared sprites
    35.         //Require sprite pixels per Unit = Texture width or height
    36.         GameObject sprite = Instantiate<GameObject>(TileToDraw);
    37.         sprite.transform.localScale = new Vector3(widthSprite, heightSprite, 1f);
    38.      
    39.         for (int x = 0; x < this.horizontalTiles; x++)
    40.         {
    41.             for (int y = 0; y < this.verticalTiles; y++)
    42.             {
    43.                 GameObject.Instantiate(sprite, new Vector3(cornerX + x * widthSprite, cornerY + y * heightSprite, 0f), Quaternion.identity);
    44.              
    45.             }
    46.         }
    47.  
    48.         Destroy(sprite);
    49.     }
    50. }
    In the script, set horizontal and vertical number of tiled sprites on screen.
     
  3. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @Sendatsu_Yoshimitsu

    Hi there.

    There are multiple problems in your approach to this thing;

    A. Where are you going to draw your tiles?
    You don't show the type of your object, but I guess it's gameobject? It might be easier to just draw tile sprites to fill the canvas instead of filling camera frustum with gameobjects, unless there is some specific reason to fill actual 3D space camera.
    You have to then expect that 2D orthographic camera is used, if you are drawing simple 2D tilemap, like it seems, you are not accounting for camera orientation.

    B. Depth. I think you don't get proper with .depth, maybe should use Camera.main.nearClipPlane... but then again, you can leave it as zero because you are using ortho camera. And upperRightScreen Vector3 is missing one value.

    C. Your for loop might not work, I'm generally bad with loops, but I think you should calculate ortho camera width and then divide it by your tile width, then you get your x amount of tiles. Same for Y tile amount. You probably have to add padding to each side, since 4x4 tiles might leave gap on side/top/bottom depending on your screen size, so instead draw 5x5 or 6x6 tiles to add some extra tiles it the screen width isn't divisible by tile size.

    EDIT: I was late... it seems.
     
  4. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Ooh, that works perfectly, thank you guys! I don't know why, but vector math just tends to short-circuit my brain in the worst of ways. For anyone else who will find it helpful, to answer your original questions:

    The tile is a prefab, it's a sprite, boxcollider, and monobehavior. The tiles are all of uniform size (64x64 pixels, imported so that 64 pixels = 1 unity unit), viewed through an orthographic camera. Since I set them up to each occupy 1 unit of space, I have the leisure of using the map array's indices as literal coordinates instead of multiplying them.

    It's for a tiled game, the reason I'm using individual gameobjects instead of a single batch-rendered sprite is because each tile is mutable and can change pretty frequently. To keep from melting the cpu with gameobject/monobehavior overhead, I'm only drawing enough tiles to fit in the frustum, then re-positioning them (and changing their sprite) every time the camera moves.