Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[RELEASED] PixelSurface: Efficient Destructible 2D Terrain

Discussion in 'Assets and Asset Store' started by JoeStrout, Nov 13, 2015.

  1. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    Update: just wanted to thank you Joe for the help, I’ve moved all my prefabs to a child object of pixel surface. The bombs work perfect now. However now I’m stuck on a new problem. What would you suggest to do to make my character object be able to stay on top of the ground when he collides with it? I was using box collider 2d but obviously that won’t work anymore. Looking forward to your response. Thanks!
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Right, what you want to do now is change your character controller so that it checks the terrain below it using surf.GetPixel (where surf is a reference to your pixel surface). Start with checking a single pixel below the character's feet, but then you'll probably want to extend it after testing to check several pixels to make sure you don't miss any narrow obstacles.
     
  3. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    I’ve done that, what I don’t know how to do is keep the object afloat above the ground. It checks under his feet but he still falls through cause idk what to do after the collision is detected.
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Well, basically, don't let your character fall any further at that point. Details depend on how your character controller is written. But maybe instead of filling up the PixelSurface thread with that, we should take this to a new thread in Getting Started? Just @ mention me there, and I'll be sure to reply.
     
  5. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    Thank you Joe for your help. I've finally solved my problem with character awareness. Another issue though I have is something that I feel is a lot more relatable to other pixel surface users. Optimization is a big thing for a games performance. I profiled my game and when there is a lot of bombs that are destroying the pixel surface, my frames go down to 15 fps! I am using a lot of pixels and I know you can configure your number of tiles to make it preform better. Do you have any tips/advice for optimizing pixel surface? I
     
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    If you are processing a whole ton of pixels at once, that will inevitably chew up some CPU time. General optimization advice applies: reduce the number of particles (in this case, pixels) if you can, be sure to test in a built app and not just in the editor, run the profiler to see exactly where the hot spots are, etc.

    Tile size probably doesn't matter all that much in this case, but you could certainly experiment with that and see if it helps.
     
  7. subvocalizer

    subvocalizer

    Joined:
    Dec 19, 2018
    Posts:
    1
    Great tool that even a lowly electrical engineer could bludgeon in to doing cool stuff.

    I've been trying to come up with a good approach to saving/transferring the terrain state though. I've taken a couple of obvious steps to make it more efficient. I've reduced the pixel data I'm transferring to just "off"/"on", so its like a bunch of bits or boolean values. And right now I'm sending the data as lines of active pixels (two ints for starting coordinates, one int for length of line). It works but I feel there has to be another method of grouping the data or a tool in Unity that is better. Any thoughts on this?
     
    JoeStrout likes this.
  8. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    One idea - probably not optimal, might be to actually save out the image of the terrain, and the states of dynamic pixels, if there are any. That means just applying that saved image as the level on a reload of the save.

    There are probably better, more efficient ideas (for instance just keeping a solid/empty state like your saying).
     
    JoeStrout likes this.
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Yeah, @subvocalizer you've basically reinvented Run-Length Encoding, and that ain't so bad.

    But I think @MD_Reptile has a good idea — you could get the PixelSurface as an image (Texture2D), and save that out by using ImageConversion.EncodeToPNG. And then to load it, do the reverse: load the PNG into a texture, and apply it to the PixelSurface.
     
  10. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    738
    I wish there is a video preview to see before I buy.

    I know there is a WebGL Demo, but somehow it never works for me..
     
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    It appears in this video that came out just a couple months ago:
     
    MD_Reptile likes this.
  12. schmosef

    schmosef

    Joined:
    Mar 6, 2012
    Posts:
    852
    Hi @JoeStrout, Just bought this asset to play around with some pixel drawing ideas.

    I'm using Unity 2019.1.0f2 and I'm finding that the Editor is freezing after a few minutes, when I run your demo scene. It's not crashing, just freezing.

    Have you tested with more recent versions of Unity?

    Is there preferred Player settings for script runtime/backend/API compatibility level?
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I haven't tried it in 2019 yet. I have not seen the behavior you describe, either. The code is pretty much plain old C#, except for the bits reading and writing textures. It should work with any runtime, backend, and API compatibility level.

    Does it freeze at random, or when you do some particular operation? Can you reproduce it in a built app — and if so, maybe attach the profiler to see where it is stuck? Finally, what platform are you testing on?
     
    schmosef likes this.
  14. schmosef

    schmosef

    Joined:
    Mar 6, 2012
    Posts:
    852
    I'm using Windows 10.

    I have a feeling that the freeze happens after I start playing with flood fill.

    It works at first but, when I keep using it, the Editor freezes. But it could be random and it could just be a Unity issue. 2019 is brand new, after all.

    I'll play with it some more, later today, to see if I can isolate the issue better.

    It would be great if you could do some testing with 2019. For one thing, the text labels on the checkbox action selectors don't render properly. I was meaning to look into it but the Editor kept freezing...
     
    Last edited: Apr 24, 2019
  15. schmosef

    schmosef

    Joined:
    Mar 6, 2012
    Posts:
    852
    After doing a lot of testing, I don't think my Editor freezing issue is related to this asset.

    I created a new, empty project and manually uninstalled all the default packages that Unity automatically includes, before importing the PixelSurface asset.

    I tested with both Mono and IL2CPP scripting backends (both with the .Net 4 runtime) and the demo scene runs with no issues.

    Sorry for raising a false alarm.

    Also, I figured out the issue with the checkbox labels. It seems the Font for some of the Text components is not set or set to a font that isn't included with the asset. I manually set those Text components to use Arial and they render now. Not sure if this is an issue with the asset package or if it's specific to importing to the latest versions of Unity. But it's an easy fix.

    Thanks for developing this asset. It looks like it will be a lot of fun to play with.
     
    JoeStrout likes this.
  16. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Thank you for the follow up! At my next update of the asset (probably in the next month or two), I'll check those fonts and probably switch to Arial.
     
  17. ben-hirsh-gripable

    ben-hirsh-gripable

    Joined:
    Jan 22, 2019
    Posts:
    7
    Hey Joe,
    Great asset you've got here, making a falling sands game clone with it.
    Just one problem we are facing: How do we get the Pixel Surface to match the viewport exactly?
    Seems that setting the Total Width and Height to:
    Code (CSharp):
    1. Screen.Width
    2. Screen.Height
    or
    Code (CSharp):
    1. Camera.main.pixelRect
    doesn't quite work.
    Any ideas?
     
    Last edited: Apr 30, 2019
    JoeStrout likes this.
  18. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    That should work if your camera is set up to give you 1 world unit == 1 pixel. Or if you use some other scaling factor (e.g. 100 units per pixel is pretty common), then multiply/divide accordingly.

    Though I haven't tried it yet, some of the utilities in the new 2D Pixel Perfect package might be useful for a SpriteSurface game, too.
     
    MD_Reptile likes this.
  19. ben-hirsh-gripable

    ben-hirsh-gripable

    Joined:
    Jan 22, 2019
    Posts:
    7
    I got it to work! Thank you!

    I'm now trying to save and load the full-screen pixel surface to playerprefs. It currently works fine unless a tile is empty. In which case, the loaded texture becomes an undesirable grey tile on the surface (interacting with falling pixels).

    This probably happens because I fill a new Texture2D with deserialized data from a stored PNG for each tile. I'm assuming because the stored PNG is empty, nothing gets written to the new Texture2D which is why the tile shows up as grey.

    I've tried checking for empty byte arrays or data strings and also checked the colors/transparencies of the tile textures.

    Do you have any alternative solutions?

    Thanks!
     
  20. ben-hirsh-gripable

    ben-hirsh-gripable

    Joined:
    Jan 22, 2019
    Posts:
    7
    Also - is there a way of clearing all falling pixels which are still in motion? I'm saving state on Application close and pixels mid-fall are getting saved in place!
     
  21. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hmm. I'll confess, saving the state of a PixelSurface isn't something I've given much thought — I don't think anyone's ever asked for it before! I assume you're using GetTileTexture to get each texture upon saving? The trouble is, this will return the tile texture, even if it's not actually in use (each tile can display a solid color if that's all it contains, in which case the texture isn't used, saving texture RAM and GPU bandwidth).

    I think what we really need is something like GetTexture, which builds a Texture2D of the entire surface (perhaps without LivePixels) at once. Then you could save this to PNG, and restore it with the existing DrawTexture method. This would solve the second problem above, too.

    I'm preparing a PixelSurface update now... I could add that in. I just need to survive the next week (lots of deadlines coming up here), and then I'll be able to do that. PM me, and we can work together to hammer that out even before the next release.
     
    schmosef likes this.
  22. ben-hirsh-gripable

    ben-hirsh-gripable

    Joined:
    Jan 22, 2019
    Posts:
    7
    Hey Joe,
    Thanks for getting back to me. I managed to fix both my issues albeit with hacky solutions:
    • I discovered the grey tiles' full RGBA was exactly (0.8039216, 0.8039216, 0.8039216, 0.8039216), so I just checked all the pixels in each tile until one which didn't have 0.8039216 as its alpha came up to deduce if a tile would save completely grey, and skip it. When loading these skipped tiles, I just fill them with Color.Clear and it works!
    • The saving in midair turned out to be quite simple. Just iterating through every pixel and calling Surface.ClearLivePixels on each one did the job. The issue was getting it to run in time on Application Close (because the Update loops will be skipped). My workaround was to add a public 'Refresh' function to PixelSurface which clears all the live pixels and then manually triggers Update() and LateUpdate().
    Hopefully this will be useful for the asset update!
     
    JoeStrout likes this.
  23. ben-hirsh-gripable

    ben-hirsh-gripable

    Joined:
    Jan 22, 2019
    Posts:
    7
    Would it be possible to implement a 'number of pixels parameter' in the 'CreateLivePixel()' function? Currently to get a bigger rate of pixels, I'm nesting the function in a for loop every update frame.
     
  24. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    If we added a method to create a bunch of LivePixels at once, it would simply call the existing method in a for loop. I can't see any more efficient way to do it; everything in that method would need to be done for each one.
     
    MD_Reptile likes this.
  25. ben-hirsh-gripable

    ben-hirsh-gripable

    Joined:
    Jan 22, 2019
    Posts:
    7
    Hey buddy,
    What's happening with the new update?

    Was wondering if you knew a more efficient way of calculating the total number of filled pixels in the surface without having to iterate through all of them and checking their Color values every frame?
    Thanks!
     
  26. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Re. new update: I've been using Pixel Surface a lot in Mini Micro, and this has led me to some improvements in Pixel Surface itself, such as the ability to set its sortOrder. There are a few more that I know I'm going to need, like drawing polygons and lines thicker than one pixel. As soon as I get those squared away, I'll pack it all up and submit an update to the asset store.

    But I don't want to hold anybody up — if you have a project that relies on these new features, PM me and we'll see what we can do.

    Over the whole surface? Hmm. Well you could certainly do a bit better by getting the Texture2Ds for each tile, then calling GetPixels32 on those and zipping through the array yourself. If you go through the PixelSurface API, it has to do extra steps for each pixel that you would avoid this way. It's still going to be a bit expensive, though.

    It might be better to instead update whatever code is filling these pixels in the first place, to maintain a count as it goes.
     
  27. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hey guys! I was just browsing the game jams over at itch.io, and I ran across this one:

    Pixel Game Jam #1!

    Pixels? Unity? Looks perfect for PixelSurface! Why not head over to the jam page give it a try?
     
    MD_Reptile likes this.
  28. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Hi. So I just bought this asset. Really good work.
     
    JoeStrout likes this.
  29. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    A character controller in this would have been nice. If it had I would give it a full star rating.
     
  30. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Thanks for the feedback. I feel like a character controller is out of scope, and would only make the asset seem unfocused and more confusing. But I can suggest this article on 2D Animation Methods in Unity, which shows three different ways of making a character controller (any of which would work great with PixelSurface).
     
    Dark_Seth likes this.
  31. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134

    Thank. I do have an Awesome Character Controller for 2D. I am not exactly sure How to change it to check for the pixel and not normal colliders. But will look into it.
     
    JoeStrout likes this.
  32. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Dark_Seth and JoeStrout like this.
  33. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    I will submit my review on the asset store later. Thank you.

    2nd thing. May I ask how you did the rocket trail. Meaning.. setting the the pixels to go in that direction and then calculating the bounce of it? If possible a code snippet? Not your whole code.
     
  34. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
  35. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Sure. The key bit for the bouncing is here:

    Code (CSharp):
    1.             int dx = (int)Mathf.Sign(velocity.x);
    2.             int dy = (int)Mathf.Sign(velocity.y);
    3.             bool xWorks = CaveGen.IsPixelClear(x - dx, y + dy, properSurf);
    4.             bool yWorks = CaveGen.IsPixelClear(x + dx, y - dy, properSurf);
    5.             if (xWorks == yWorks) {
    6.                 if (Random.value <= 0.5f) {
    7.                     velocity.x = -velocity.x;
    8.                 } else {
    9.                     velocity.y = -velocity.y;
    10.                 }
    11.             } else if (xWorks) {
    12.                 velocity.x = -velocity.x;
    13.             } else {
    14.                 velocity.y = -velocity.y;
    15.             }
    Basically we check whether we can bounce (because it's clear) in the X and Y directions. If either we can bounce both ways, or we can't bounce either way, then we pick one at random. Otherwise, we bounce in whichever direction works.

    I don't mind sharing more of the surrounding code, though. This is my ExhaustParticle class:

    Code (CSharp):
    1. public class ExhaustParticle : Particle {
    2.    
    3.     protected override Particle NewSameType(PixelSurface surf) {
    4.         // Create a new particle of the same type as this one.
    5.         return surf.CreateLivePixel<ExhaustParticle>(x, y, color);
    6.     }
    7.    
    8.     protected override void MovingTo(PixelSurface surf, ref Vector2 newPos) {
    9.         Vector2 pos = newPos;
    10.         surf.IterateOverLine((int)position.x, (int)position.y, (int)newPos.x, (int)newPos.y, (x,y) => {
    11.             PixelSurface properSurf = surf;
    12.             if (y < 0 && surf == Exhaust.upperSurf) {
    13.                 properSurf = Exhaust.lowerSurf;
    14.                 y += surf.totalHeight;
    15.             } else if (y >= surf.totalHeight && surf == Exhaust.lowerSurf) {
    16.                 properSurf = Exhaust.upperSurf;
    17.                 y -= surf.totalHeight;
    18.             }
    19.             if (CaveGen.IsPixelClear(x, y, properSurf)) return true;
    20.             int dx = (int)Mathf.Sign(velocity.x);
    21.             int dy = (int)Mathf.Sign(velocity.y);
    22.             bool xWorks = CaveGen.IsPixelClear(x - dx, y + dy, properSurf);
    23.             bool yWorks = CaveGen.IsPixelClear(x + dx, y - dy, properSurf);
    24.             if (xWorks == yWorks) {
    25.                 if (Random.value <= 0.5f) {
    26.                     velocity.x = -velocity.x;
    27.                 } else {
    28.                     velocity.y = -velocity.y;
    29.                 }
    30.             } else if (xWorks) {
    31.                 velocity.x = -velocity.x;
    32.             } else {
    33.                 velocity.y = -velocity.y;
    34.             }
    35.             pos.x = x;
    36.             pos.y = y;
    37.             DigSurfaceAt(properSurf, x, y);
    38.             return false;
    39.         });
    40.         newPos = pos;
    41.     }
    42.  
    43.     void DigSurfaceAt(PixelSurface surf, int x, int y) {
    44.         Color c = Globals.BackgroundColor;
    45.         surf.SetPixel(x, y, c);
    46.         surf.SetPixel(x+1, y, c);
    47.         surf.SetPixel(x-1, y, c);
    48.         surf.SetPixel(x, y+1, c);
    49.         surf.SetPixel(x, y-1, c);
    50.        
    51.         RockChewAudio.clicks++;
    52.     }
    53.    
    54. }
    And here's its base class, Particle (itself a subclass of PixSurf.LivePixel):

    Code (CSharp):
    1. /*
    2. Particle is a LivePixel subclass that handles several common needs:
    3.     1. Moves to the correct PixelSurface as needed.
    4.     2. Velocity and gravity.
    5.     3. Limited lifetime.
    6.     4. Color changing over time.
    7. */
    8. using UnityEngine;
    9. using UnityEngine.Events;
    10. using System.Collections.Generic;
    11. using PixSurf;
    12.  
    13. public class Particle : LivePixel {
    14.     public Vector2 velocity;
    15.     public float gravity = 50;
    16.     public float lifetime = 2f;
    17.     public Color startColor = Color.white;
    18.     public Color endColor = Color.black;
    19.    
    20.     float startTime;
    21.    
    22.     public Particle() {
    23.        
    24.     }
    25.    
    26.     public static Particle Create<T>(int x, int y, Color startColor, Color endColor) where T : Particle, new() {
    27.         PixelSurface surf;
    28.         if (y > Exhaust.upperSurf.transform.position.y) {
    29.             surf = Exhaust.upperSurf;
    30.         } else {
    31.             surf = Exhaust.lowerSurf;
    32.         }
    33.         y -= (int)surf.transform.position.y;
    34.         Particle p = surf.CreateLivePixel<T>(x, y, startColor);
    35.         p.startColor = startColor;
    36.         p.endColor = endColor;
    37.         return p;
    38.     }
    39.    
    40.     public override void Start(PixelSurface surf) {
    41.         startTime = Time.time;
    42.     }
    43.    
    44.     public override void Update(PixelSurface surf) {
    45.         velocity.y -= gravity * Time.deltaTime;
    46.         Vector2 newPos = position + velocity * Time.deltaTime;
    47.         MovingTo(surf, ref newPos);
    48.         position = newPos;
    49.        
    50.         float t = (Time.time - startTime) / lifetime;
    51.         if (t >= 1) DieClear();
    52.         else {
    53.             color = Color.Lerp(startColor, endColor, t);
    54.             if (!surf.InBounds(position)) MoveToCorrectSurface(surf);
    55.         }
    56.     }
    57.    
    58.     protected virtual void MovingTo(PixelSurface surf, ref Vector2 newPos) {      
    59.     }
    60.    
    61.     void MoveToCorrectSurface(PixelSurface surf) {
    62.         PixelSurface correctSurf;
    63.         if (position.y > surf.totalHeight) {
    64.             position.y -= surf.totalHeight;
    65.             correctSurf = Exhaust.upperSurf;
    66.         } else {
    67.             position.y += surf.totalHeight;
    68.             correctSurf = Exhaust.lowerSurf;
    69.         }
    70.         if (surf == correctSurf) {
    71.             DieClear();
    72.         } else {
    73.             //Debug.Log("Transferring particle from " + surf + " to " + correctSurf);
    74.             Particle noob = NewSameType(correctSurf);
    75.             CloneInto(noob);
    76.             DieClear();
    77.         }
    78.     }
    79.    
    80.     protected virtual Particle NewSameType(PixelSurface surf) {
    81.         // Create a new particle of the same type as this one.
    82.         // IMPORTANT: subclasses must override this method to create the right type.
    83.         return surf.CreateLivePixel<Particle>(x, y, color);
    84.     }
    85.    
    86.     protected virtual void CloneInto(Particle noob) {
    87.         // Copy all our properties into noob.
    88.         noob.velocity = velocity;
    89.         noob.gravity = gravity;
    90.         noob.lifetime = lifetime;
    91.         noob.startTime = startTime;
    92.         noob.startColor = startColor;
    93.         noob.endColor = endColor;
    94.     }
    95. }
     
    Dark_Seth likes this.
  36. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    May I ask what is the Exhaust. A Game Object?
     
  37. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I think it's just a static class that keeps track of some global data (like the two PixelSurfaces I use to make an infinitely scrolling world).
     
    Dark_Seth likes this.
  38. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Starting to understand this framework more. I have created like a flame thrower that turns into a bouncing nightmare. LOL

     
    schmosef, JoeStrout and MD_Reptile like this.
  39. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Ok Guys. I think I am getting somewhere with the player controller.
    Using a Sprite with a boxcollider2d. I calculate some points for it to look downward when player is falling and upwards when player is jumping. Also left and right depending one movement direction.

    It works for the most part. except every now and then the player falls slightly to far into the ground.
    Again. If I get this working. Everyone can use this.

    PLEASE HELP.

    This is what happens. I have to press jump to get out of the ground. Sometimes the player lands on the ground properly and I can run.

    Controller2D.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using PixSurf;
    4.  
    5. [RequireComponent(typeof(BoxCollider2D))]
    6. public class Controller2D : MonoBehaviour
    7. {
    8.  
    9.     public LayerMask collisionMask;
    10.  
    11.     const float skinWidth = .03f;
    12.     public int horizontalRayCount = 4;
    13.     public int verticalRayCount = 4;
    14.  
    15.     float horizontalRaySpacing;
    16.     float verticalRaySpacing;
    17.  
    18.     BoxCollider2D collider;
    19.     RaycastOrigins raycastOrigins;
    20.     public CollisionInfo collisions;
    21.     public PixelSurface surf;
    22.     public GameObject surfaceController;
    23.     bool ClearAt(PixelSurface surf, int x, int y)
    24.     {
    25.         Color c = surf.GetPixel(x, y);
    26.         return c == Color.black || c.a == 0;
    27.     }
    28.  
    29.  
    30.     void Awake()
    31.     {
    32.  
    33.         surf = surfaceController.GetComponent<PixelSurface>();
    34.  
    35.     }
    36.  
    37.     void Start()
    38.     {
    39.         collider = GetComponent<BoxCollider2D>();
    40.         CalculateRaySpacing();
    41.     }
    42.  
    43.     public void Move(Vector3 velocity)
    44.     {
    45.         UpdateRaycastOrigins();
    46.         collisions.Reset();
    47.  
    48.         if (velocity.x != 0)
    49.         {
    50.             HorizontalCollisions(ref velocity);
    51.         }
    52.         if (velocity.y != 0)
    53.         {
    54.             VerticalCollisions(ref velocity);
    55.         }
    56.  
    57.         transform.Translate(velocity);
    58.     }
    59.  
    60.     void HorizontalCollisions(ref Vector3 velocity)
    61.     {
    62.         float directionX = Mathf.Sign(velocity.x);
    63.         float rayLength = Mathf.Abs(velocity.x) + skinWidth;
    64.  
    65.         for (int i = 0; i < horizontalRayCount; i++)
    66.         {
    67.             Vector2 rayOrigin = (directionX == -1) ? raycastOrigins.bottomLeft : raycastOrigins.bottomRight;
    68.             rayOrigin += Vector2.up * (horizontalRaySpacing * i);
    69.            
    70.  
    71.             Debug.DrawRay(rayOrigin, Vector2.right * directionX * rayLength, Color.red);
    72.  
    73.             Vector2 pixelPos = surf.PixelPosAtWorldPos(rayOrigin);
    74.  
    75.             if ((!ClearAt(surf, (int)pixelPos.x + (int)directionX, (int)pixelPos.y)))
    76.             {
    77.                 velocity.x = (0) * directionX;
    78.                 collisions.left = directionX == -1;
    79.                 collisions.right = directionX == 1;
    80.                
    81.             }
    82.         }
    83.     }
    84.  
    85.     void VerticalCollisions(ref Vector3 velocity)
    86.     {
    87.         float directionY = Mathf.Sign(velocity.y);
    88.         float len = 0.2f;
    89.         float rayLength = len;
    90.  
    91.         for (int i = 0; i < verticalRayCount; i++)
    92.         {
    93.             Vector2 rayOrigin = (directionY == -1) ? raycastOrigins.bottomLeft : raycastOrigins.topLeft;
    94.             rayOrigin += Vector2.right * (verticalRaySpacing * i + velocity.x);
    95.             Debug.DrawRay(rayOrigin, Vector2.up * directionY * rayLength, Color.red);
    96.  
    97.             Vector2 pixelPos = surf.PixelPosAtWorldPos(rayOrigin);
    98.             if ((!ClearAt(surf, (int)pixelPos.x, (int)pixelPos.y + (int)directionY)))
    99.             {
    100.  
    101.                 velocity.y = (0) * directionY;
    102.                 collisions.below = directionY == -1;
    103.                 collisions.above = directionY == 1;
    104.              
    105.                
    106.              
    107.             }
    108.  
    109.  
    110.  
    111.  
    112.  
    113.         }
    114.     }
    115.  
    116.     void UpdateRaycastOrigins()
    117.     {
    118.         Bounds bounds = collider.bounds;
    119.         bounds.Expand(skinWidth * -2);
    120.  
    121.         raycastOrigins.bottomLeft = new Vector2(bounds.min.x, bounds.min.y);
    122.         raycastOrigins.bottomRight = new Vector2(bounds.max.x, bounds.min.y);
    123.         raycastOrigins.topLeft = new Vector2(bounds.min.x, bounds.max.y);
    124.         raycastOrigins.topRight = new Vector2(bounds.max.x, bounds.max.y);
    125.     }
    126.  
    127.     void CalculateRaySpacing()
    128.     {
    129.         Bounds bounds = collider.bounds;
    130.         bounds.Expand(skinWidth * -2);
    131.  
    132.         horizontalRayCount = Mathf.Clamp(horizontalRayCount, 2, int.MaxValue);
    133.         verticalRayCount = Mathf.Clamp(verticalRayCount, 2, int.MaxValue);
    134.  
    135.         horizontalRaySpacing = bounds.size.y / (horizontalRayCount - 1);
    136.         verticalRaySpacing = bounds.size.x / (verticalRayCount - 1);
    137.     }
    138.  
    139.     struct RaycastOrigins
    140.     {
    141.         public Vector2 topLeft, topRight;
    142.         public Vector2 bottomLeft, bottomRight;
    143.     }
    144.  
    145.     public struct CollisionInfo
    146.     {
    147.         public bool above, below;
    148.         public bool left, right;
    149.  
    150.         public void Reset()
    151.         {
    152.             above = below = false;
    153.             left = right = false;
    154.         }
    155.     }
    156.  
    157. }
    158.  
    Player.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [RequireComponent(typeof(Controller2D))]
    5. public class Player : MonoBehaviour
    6. {
    7.  
    8.     public float jumpHeight = 4;
    9.     public float timeToJumpApex = .4f;
    10.     float accelerationTimeAirborne = .2f;
    11.     float accelerationTimeGrounded = .1f;
    12.     public float moveSpeed = 2;
    13.  
    14.     float gravity;
    15.     float jumpVelocity;
    16.     Vector3 velocity;
    17.     float velocityXSmoothing;
    18.  
    19.     Controller2D controller;
    20.  
    21.     void Start()
    22.     {
    23.         controller = GetComponent<Controller2D>();
    24.  
    25.         gravity = -(2 * jumpHeight) / Mathf.Pow(timeToJumpApex, 2);
    26.         jumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
    27.         print("Gravity: " + gravity + "  Jump Velocity: " + jumpVelocity);
    28.     }
    29.  
    30.     void Update()
    31.     {
    32.  
    33.         if (controller.collisions.above || controller.collisions.below)
    34.         {
    35.             velocity.y = 0;
    36.         }
    37.  
    38.         Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
    39.  
    40.         if (Input.GetKeyDown(KeyCode.Space) && controller.collisions.below)
    41.         {
    42.             velocity.y = jumpVelocity;
    43.         }
    44.  
    45.         float targetVelocityX = input.x * moveSpeed;
    46.         velocity.x = Mathf.SmoothDamp(velocity.x, targetVelocityX, ref velocityXSmoothing, (controller.collisions.below) ? accelerationTimeGrounded : accelerationTimeAirborne);
    47.         velocity.y += gravity * Time.fixedDeltaTime;
    48.         controller.Move(velocity * Time.fixedDeltaTime);
    49.     }
    50. }
    51.  
     
  40. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    From a quick read-through, it looks to me like when your ray hits something, all you are doing is stopping the fall, wherever you happen to be. You're not using the information about where the ray hit something to adjust your Y position so that your feet are on the ground.
     
  41. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Ok Guys. So I went back to my original controller.
    Added cool adjustments to the platform ;)
    CLICK ON IMAGES TO MAXIMIZE
    1. Snow / Lifeparticles that fall will become part of the word that ANY collider can interact with. :p
    2. Explosions particles flies around and again once settled it again becomes part of the word
    3. I can add effect on player/enemies according to type.


    Player Controller hat I use in any other 2d game works perfectly. with no Pixel comparison needed.


    I am generating a huge World and I still get 450+ frames per second.

     
    MD_Reptile, JoeStrout and mgear like this.
  42. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Fantastic! Looks like you've got it all figured out, and are making a very cool game, too. Can't wait to play it! :D
     
    Dark_Seth likes this.
  43. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Just a question. My pixel word size is 4096 x 4096. Runs about 500+fps as I have been tweaking some things.

    I'll Post an update on my Character Controller for this that runs so smooth now :)

    So when I try and use the Script above the frames die on me. down to like 13FPS.

    The problem I found is looping thru the whole array in the grid. That is 16 777 216 pixels. And then an Second time to update.

    Code (CSharp):
    1. for (int y=0; y<height; y++) {
    2.             for (int x=0; x<width; x++) {
    3.              
    4.                 float remainingMass = mass[x, y];
    5.                 if (remainingMass <= 0) continue;    // ToDo: kMinMass?
    How would I go about only optimizing the script to only look at active water grid entries and not blank ones?

    Regards
     
  44. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    You would have to restrict the water to only certain parts of your world. And then loop over just those parts.

    You could also spread the work out, e.g. by doing it in a coroutine that yields every 4000 pixels or whatever. But of course this will make the water update much more slowly.

    Per my comments on that post, I'm not sure this is really the best approach to fluid simulation. I still think that second idea I described (but never implemented) might work much better.
     
  45. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Thanks. Will have to Really thing About that..... My Idea was when it rains like below the splat drops has a random drop of leaving water drops behind.

    Had to draw head and tail pixel of raindrop. I have no Idea other than that how to group pixels together... If you have an Idea on this Please let me know.

    Also see update on character controller. Walking is as smooth as silk ;). No Animations for now.

    This is my last post here. Not to dump everything on you Post. I will create a new thread and post it here.

     
  46. Euro

    Euro

    Joined:
    Apr 23, 2014
    Posts:
    2
    Awesome! Do you have this on github? I'm making a worms clone and this would be helpful for figuring out the character controller.
     
  47. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    It a normal build-in Unity character Controller. Nothing Special about the controller.
     
  48. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    You are saying you used the built in character controller in this situation? I mean... that can't be right?

    Certainly you must have wrote a script that checks for collisions completely separate of the built in physics, and I would assume you did your own locomotion too, with velocity or just moving around and jumping on x and y without anything to do with the built in character controller... I don't see how one could implement a built in character controller in PixelSurface or really any game with per-pixel collisions and no colliders...
     
  49. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Yes it is s normal caharcter controller.I will post some screen shots tomorrow.
     
  50. Dark_Seth

    Dark_Seth

    Joined:
    May 28, 2014
    Posts:
    134
    Ok. here you go. Normal Character Controller. I am busy working on World generation. Got some nice looking effect with Softsand,Hard Sand, copper, Iron, Gold in there. I am currently adding pixelType to each pixel. Gonna Use that for type rather than the color.

    This is using some this is currently blending 3 perling noice and gradient to get the world height and look.

    The trick was not to change the Character controller but to add functions to the Pixsurface.
    You will notice rain goes thru the player not the world. The Player Logs to Console. "Player wet". Fire type will set player on fire etc...
    Sand goes thru the player until it settles . The Character Contoller pushes upwards. ( Default Controller )

    Controller respects Slope limit and step limits too.

    This is how it look. I was playing around with Raycast to simulate a gun.

    GIf file just show broken when I link it. It seems to be to wide. Link Below

    https://i.imgur.com/cgyt3Iu.gif
     
    MD_Reptile and Euro like this.