Search Unity

[RELEASED] PixelSurface: Efficient Destructible 2D Terrain

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

  1. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hey pixelers! There is a 2-week game contest going on to make a 2D top-down racing game! This might be a great application of PixelSurface... check out the details here!
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    FYI, I've uploaded a minor update to the Asset Store. It fixes two issues that could occur when your drawing (particularly FillEllipse) is large relative to the tile size.

    I'll let you know when the new version is approved, but meanwhile, if anybody's seeing any such weirdness, PM me and I'll send you the fixed files.
     
    MD_Reptile likes this.
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    And... the new version was approved this week! Please check it out!
     
    KristianDoyle likes this.
  4. adampound

    adampound

    Joined:
    Jul 14, 2014
    Posts:
    28
    is there a way to determine if the live pixels are done moving?
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Live pixels only move however you program them to. So, when one is "done moving" is up to you.
     
  6. adampound

    adampound

    Joined:
    Jul 14, 2014
    Posts:
    28
    I'm trying to use the GoBoomAndCollapse script from front page(I know you didn't write it). It works well but it appears after it collapses it looks like they overlap each other for a bit then re correct themselves. if I have an object sitting on top of the collapsed terrain and it fall beneath the live particles. I also tried doing it without making them live and just moving it down but its almost like its not seeing the cleared radius. If you could assist that would be great

    Code (CSharp):
    1.  public void ExplodeAndCollapse(int x, int y, int radius = 20)
    2.     {
    3.         if (surf == null) surf = GetComponent<PixelSurface>();
    4.        
    5.         Vector2 center = new Vector2(x, y);
    6.  
    7.         Rect r = new Rect(x - radius, y - radius, radius * 2, radius * 2);
    8.         surf.FillEllipse(r, Color.clear);
    9.         float radSqr = radius * radius;
    10.  
    11.         // collapse above
    12.         Rect r2 = new Rect(x - radius, y, radius * 2, surf.totalHeight - y);
    13.         bool changed = true;
    14.         while (changed)
    15.         {
    16.             changed=false;
    17.             for (int i = (int)r2.xMin - 3; i < (int)r2.xMax + 3; i++)
    18.             {
    19.                 for (int j = (int)r2.yMin; j < (int)r2.yMax; j++)
    20.                 {
    21.                     Color c = surf.GetPixel(i, j);
    22.                     Debug.Log(i);
    23.                     if (c.a == 0f) break; // empty, continue to next line
    24.                    
    25.                         // dont add falling pixel, if inside explosion circle
    26.                         //float dsqr = (i - x) * (i - x) + (j - y) * (j - y);
    27.                         //if (dsqr > radSqr)
    28.                         //{
    29.                             if (surf.GetPixel(i, j - 1).a == 0f)
    30.                             {
    31.                                 surf.SetPixel(i, j, Color.clear);
    32.                                 surf.SetPixel(i, j - 1, c);
    33.                                 changed = true;
    34.                             }
    35.                         //}
    36.                    
    37.                 }
    38.             }
    39.         }
    40.  
    41.         // clear original explosion hole
    42.        
    43.     }
     
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I'm sorry, I don't quite understand what you mean:

    Can you maybe post a picture or two to illustrate what you're talking about here?

    What sort of object? Are you talking about a Sprite?
     
  8. adampound

    adampound

    Joined:
    Jul 14, 2014
    Posts:
    28
    1.Here are the pictures from start to finish


    2. Yes a sprite
     
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hmm. That's really strange. I definitely think you should use the LivePixel approach of the original code; doing it the way you've attempted above, you would need to use a Coroutine or something to continue moving pixels down each frame so the player can see it. That's doable but it would get fairly complicated, and probably not be as efficient as using LivePixels.

    But I don't understand why you're getting pixels initially overlapping and then rebounding. I didn't see such an effect in @mgear's demo. Perhaps he'll have some insight on it?
     
  10. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,411
    I think it did happened there also, it felt like the dropped pile is not as high as it should,
    so probably some get overlapped. (didnt _dig deeper_ to see whats happening)
     
    JoeStrout likes this.
  11. adampound

    adampound

    Joined:
    Jul 14, 2014
    Posts:
    28
    I got it working better(still needs some tweaking). I created my own PixParticle class and removed the checking for left and right in update

    Code (CSharp):
    1. public class MyPixParticle : LivePixel
    2. {
    3.     static public float gravity = 50f;
    4.  
    5.     public Vector2 velocity;
    6.  
    7.     public bool ClearAt(PixelSurface surf, int x, int y)
    8.     {
    9.         Color c = surf.GetPixel(x, y);
    10.         return c == Color.black || c.a == 0;
    11.     }
    12.     public override void Update(PixelSurface surf)
    13.         {
    14.  
    15.             velocity.y -= gravity * Time.deltaTime;
    16.             velocity *= 1f - 0.1f * Time.deltaTime;
    17.             position += velocity * Time.deltaTime;
    18.  
    19.             if (!ClearAt(surf, x, y))
    20.             {
    21.                
    22.                 if (velocity.y < 0)
    23.                 {
    24.                  
    25.                     position.y++;
    26.                     Die();
    27.                 }
    28.                 velocity = Vector2.zero;
    29.             }
    30.         }
    31.     }
     
    JoeStrout likes this.
  12. RecursiveRuby

    RecursiveRuby

    Joined:
    Jun 5, 2013
    Posts:
    163
    Hey Joe,
    I'm not sure if its been mentioned or not but I figured a quick question couldn't hurt. I've been implementing my own solution like this since I have an idea for a game I would really like to make. Mine largely falls short in terms of performance when it comes to generating colliders. Does your solution generate colliders that will work with rigibodies and the likes and if so what sort of performance hit are we looking at? Would it be viable to generate objects during runtime.

    Looks great anyway very interested in this, all the best.
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    It does not generate colliders. I haven't needed them — instead, when I want to detect collision with a PixelSurface (as in Rocket Plume), I just test pixels.

    But then, I rarely use Unity's physics engine, because in most games it's far more trouble than it's worth. If you want to describe your game idea, I'd be happy to kick around implementation ideas with you!
     
  14. RecursiveRuby

    RecursiveRuby

    Joined:
    Jun 5, 2013
    Posts:
    163
    So the idea is a 2D space game where you fly around mining asteroids and building colonies. I want the ship you fly around in to be a rigidbody so it gives that smooth movement. Then you'll chip away at the asteroid with bullets that I would test for collision. Also I'd want the ship to bounce off the asteroids if it were to hit them.
     
  15. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Have you looked at Rocket Plume? It's basically the same physics — flying around in a ship (smooth movement), and chipping away at the surrounding rocks (with the plume in our case; with bullets in your case).

    You don't need colliders, nor the Unity physics engine, for that. And believe me, you're much better off without it — wrestling the physics engine to do what you want in a case like this is much harder than just coding it yourself.
     
  16. RecursiveRuby

    RecursiveRuby

    Joined:
    Jun 5, 2013
    Posts:
    163
    I've see the pics of it but I don't see any videos. Maybe I'm being stupid and missing something idk. I have an idea how the game would play though. I don't know a huge amount about implementing physics outside of what unity already provides. Although I do have an idea. The thing that would largely get me would be custom raycasts to predict collisions I guess. Is that how you would do it? I definitely do see the benefits of not using unity's system for a case like this.
     
  17. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Ship physics is really easy: you just keep a velocity vector, which you update from control inputs, and then use to update the position. Here's the ship physics script from Rocket Plume:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Events;
    3. using System.Collections;
    4.  
    5. public class RocketPhysics : MonoBehaviour, GameStateChangeReceiver {
    6.    
    7.     public float acceleration = 1;
    8.     public float dragConst = 0.1f;
    9.     public float gravity = 1;
    10.    
    11.     public Vector2 velocity;
    12.    
    13.     public UnityEvent onOutOfFuel;
    14.    
    15.     Vector3 initialPosition;
    16.     Exhaust engine;
    17.     ScoreAndFuel scoreAndFuel;
    18.    
    19.     void Awake() {
    20.         GameStateManager.AddChangeReceiver(this);
    21.         initialPosition = transform.position;
    22.         engine = GetComponent<Exhaust>();
    23.         scoreAndFuel = GetComponent<ScoreAndFuel>();
    24.     }
    25.    
    26.     void Update() {      
    27.         Vector2 thrust = Vector2.zero;
    28.         if (engine.on) {
    29.             if (scoreAndFuel.fuel <= 0) {
    30.                 // Out of fuel, but the throttle is still on...
    31.                 // inform the user that we're out of fuel.
    32.                 onOutOfFuel.Invoke();
    33.             } else {
    34.                 thrust = acceleration * (Vector2)transform.up;
    35.             }
    36.         }
    37.         Vector2 drag = -velocity.normalized * velocity.sqrMagnitude * dragConst;
    38.         Vector2 grav = Vector2.down * gravity;
    39.         velocity += (thrust + drag + grav) * Time.deltaTime;
    40.        
    41.         transform.position += new Vector3(velocity.x, velocity.y, 0) * Time.deltaTime;      
    42.     }
    43.        
    44. }
    45.  
    Note that acceleration is a public vector here; it's set elsewhere, by the input script, but I think you get the idea.

    As for detecting collisions, there's no need to raycast. We just check a small square of pixels around the ship position. It looks like this (in a separate ShipDamage script):

    Code (CSharp):
    1.         int x = Mathf.RoundToInt(transform.position.x);
    2.         int y = Mathf.RoundToInt(transform.position.y - surf.transform.position.y);
    3.        
    4.         for (int i=-checkRadius; i<=checkRadius; i++) {
    5.             for (int j=-checkRadius; j<=checkRadius; j++) {
    6.                 if (!CaveGen.IsPixelClear(x+i, y+j, surf)) damage += damagePerHit;
    7.             }
    8.         }
    9.  
    ...where CaveGen.IsPixelClear is just a little static helper method in the script that generates the caves (and so knows what colors constitute "rock" and "no rock"). It looks like this:

    Code (CSharp):
    1.     public static bool IsPixelClear(int x, int y, PixelSurface surf) {
    2.         Color c = surf.GetPixel(x, y);
    3.         return (c.r + c.g + c.b < 0.4f);
    4.     }
    5.  
    In other words, in our particular game, a pixel is considered 'clear' if it is very dark (sum of red, green, and blue is less than 0.4). You might have a different rule in your game.
     
    MD_Reptile likes this.
  18. RecursiveRuby

    RecursiveRuby

    Joined:
    Jun 5, 2013
    Posts:
    163
    Ah that makes a lot of sense! thanks for that really appreciate it! Will probably pick this up later in the week :)
     
  19. valentinwinkelmann

    valentinwinkelmann

    Joined:
    Nov 3, 2014
    Posts:
    190
    Can Images used in this? i mean can i load a sprite image and let it destruct pixel by pixel with this system?
     
  20. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Yes. Place any image into the proper slot and it can be loaded as the level.
     
    JoeStrout likes this.
  21. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    In addition, there is a DrawTexture method, that lets you draw any rectangular portion of a Texture2D into any rectangular area of a PixelSurface. So you could use this to plop down a sprite into a PixelSurface for destruction (or gathering snow, or whatever else you might need).
     
  22. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hey all, there's a thread over in General Discussion where a poster is lamenting the lack of good pixel-based Terraria-style sandbox games to play.

    I only tried Terraria once some time ago, but as I recall it is tile-based. It might be interesting to try something like that but entirely pixel-based.

    You could assign a unique color (or several colors, for cosmetic variation) to each unique material in the world. Your world-generator would lay these out in sensible ways: dirt with occasional rocks topped with grass, etc. The player would be a sprite exploring and digging in this world.

    Some material types could be seeds, which could grow into plants. I might approach it this way: on each frame, pick a random X value (i.e. column of the world), and zip down from the top (sky) until you hit a non-transparent material type. Then if you hit dirt or grass, go a little further, and see if you hit a seed material. If so, sprout the corresponding plant (spawning root and plant materials).

    Or you could treat seeds as entities, i.e. objects separate from the pixel world that do their own thing, just like monsters etc. Just give them a low priority so they only "wake up" and check their environment now and then.

    You could have rain, which would be implemented as LivePixels that fall from the sky. Where they hit they run downhill, like some of the water demos we've explored here. Or when they hit certain types of materials, they make things happen: dry dirt turns into wet dirt, etc. This could lead to players digging channels or pipelines to direct water where they need it (or other liquids like lava).

    Building would be the trickiest part in a pixel world. I guess you'd need some sort of crafting system that lets the player make blocks, and when the player places a block, you just check all the pixels he's replacing to make sure it's kosher, then plop it down. I might also align the placement onto a regular grid at this point, just to make it easier for the player to make nice neat structures... though perhaps if they hold Shift or something, you turn this grid-snapping off, allowing them to place their blocks arbitrarily. (Try that in Minecraft or Terraria!)

    I think this could be a really fun game, and fairly easy to make. What do you all think?
     
    MD_Reptile likes this.
  23. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    I'd absolutely love to see that myself :D

    EDIT: And.... I'd love for that to push PixelSurface further... and get some new interesting features out of the box! That rain idea would be really cool to see for sure, and probably lots of other neat mechanics could come out of liquids and pooling of water.
     
  24. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I don't see those as needing any new PixelSurface features. Do they?

    But maybe you mean more demo code... and yeah, I could see that.
     
  25. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    I can't think of anything other than ACID RAIN :eek: - because of the features of PixelSurface. Could be an interesting base for a concept - Explorer/Survival/RPG-ish, have to explore on a planet that is constantly Acid Rain - and eats away at certain areas of the ground. Other areas are metal rocks, and each day the ground recovers/re-grows itself in the 1 hour the rain stops. Have to return to base to cleanse the suit or it will pop a leak - and you die! Each trip you have to go out farther and farther to explore/solve puzzles/mysteries etc.
     
    JoeStrout likes this.
  26. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I like it!

    Just brainstorming further, what if plants had evolved that were immune to the acid rain? So if the rain hits greenery, nothing happens; but if it hits naked soil, the soil dissolves away. Sort of like real-life soil erosion, but much faster.

    So now you have reason to (1) avoid cutting down plants (but you need those plants to make stuff!) as much as possible, and (2) plant seeds, and somehow protect them from the rain until they grow big enough, at least anyplace you want to keep the soil.

    Sounds like fun to me!
     
    theANMATOR2b likes this.
  27. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    And at certain time/areas you can access caverns created by the acid rain, only at certain times when the suit is most susceptible to damage. Once able to access the caverns - have to avoid or defeat subterranean beasts who see you as a threat to there survival. They are down there because of the acid rain. But you need there energy source to power up your ship to get off the planet.

    :)

    Or vampires!

    The acid rain drips down in certain areas into the caverns to mix with stagnant water to create a gelatinous type substance that feeds the underground plants (which are carnivorous and explode into an acid bath upon death).
     
    JoeStrout likes this.
  28. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Sounds great! I certainly don't have time for any more projects, though. Somebody grab this and run with it! I'll be here to support all your pixel surfacing needs. :)
     
  29. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,790
    If this isn't created in the next 2 years - I will take the burden upon my shoulders and build it to the specs outlined. ;) Other tasks have priority right now - but this is a free 'idea' for those who post about not having any ideas.
    In 2 years if not already created - I'll need some support Joe, though I might have learned how to code by then - proly not. :oops:
     
    MD_Reptile and JoeStrout like this.
  30. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    9 new Pixel Surface users took advantage of the May Madness Sale! (Normally it's not easy for asset sellers to get actual sales numbers for a specific asset, but Unity was kind enough to send out this info just now.)

    So, to the newbies: welcome! We're glad you're here. Please have fun with Pixel Surface, ask any questions you have, and don't be shy to share what you're doing with it!

    (Especially if you can include screenshots. We love screenshots.)
     
    MD_Reptile likes this.
  31. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hey all, I just became aware of this recent blog post about a cellular automata liquid simulation. It's basically the same thing we discussed here in this thread (on the previous page, I think), but nicely done and written up.

    Anybody want to try implementing this with PixelSurface?
     
  32. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Seems cool - but I wonder if its a problem that the demo gif on the page seems to show "partially filled" pixels that are not entirely "full" of water. Perhaps this could be represented by shading instead? Or just not represented at all, and have each pixel either entirely "full" if its over 50 percent, and entirely "empty" if its less?
     
  33. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I was thinking you'd probably use shading — 10 very subtly different shades of blue for 10 different levels of water.

    Users probably wouldn't even notice, to the extent that it's visible at all, it would act like anti-aliasing (if you select the colors well).
     
    MD_Reptile likes this.
  34. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Check out this game somebody is making with similar features, and even really nice looking water physics! No idea if this is done in unity or not, just thought I'd share :)

    https://noitagame.com/

     
    JoeStrout likes this.
  35. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    However you do it... pixels are fun! :D
     
    MD_Reptile likes this.
  36. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hey pixel people! The Unity 2D Challenge is underway. Using PixelSurface might make your game stand out from the crowd!
     
  37. Moever

    Moever

    Joined:
    Dec 14, 2013
    Posts:
    2
    Hi all,

    I finally decided to buy this package to help me create a game which I played a long long time ago with my father ( on the Amiga I think ) : Tank wars (Bomb.exe).

    Have played with it a couple of hours and was able to create a small, couch coop/ network based (with photon) prototype of the game. You can see the first session here :


    Will be planning out a full fledged version in a couple of days and start working on it - for now, thanks a lot for the package and contributions in this thread ( particularly the cellular automata liquid simulation and collapse scripts! ).

    Marius
     
    MD_Reptile and JoeStrout like this.
  38. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    That's super fun! I'm glad PixelSurface helped you recreated this gem from your childhood. I like the music too! :)
     
  39. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    Hey everyone,
    I just bought pixel surface and it’s a great add-on. However with my game I need some help. As you all probably know, pixel surface uses pixel color to detect a collision. In my game you are a soldier trying to dodge bombs falling from the sky. I needed pixel surface to make a crater for when the bomb hit the surface. The issue I have now is that before pixel surface I used generic box colliders to test collisions, now it seems I need to adapt all of my objects to test for pixel color. Can this be done by simply changing my collision detection to pixel color?
     
  40. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I think you're on the right track! What sort of code are you using to control your character? I would expect it has something like a raycast (or several) to find where the "ground" (defined by colliders) is located below your soldier. You would just change that code to instead check a line of pixels. The rest of the character controller should be unchanged.
     
  41. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    I am using the Touch UI to move because I am developing it for iOS devices. And yes right now I am. That sounds easy enough though to change to pixel detection. Also, I saw in the demo that you had to put the bomb sprite in as a child of the pixel surface object. When I try to do that for mine it won’t work. It may be because I have a rigid body attached to my bomb sprite or because it’s a prefab. Any suggestions? Thanks!
     
  42. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    No worries, you can organize your object hierarchy in whatever way makes sense to you. Just give your script a public PixelSurface property, and drag your surface in, and then it can easily access the pixels.
     
  43. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    Does it have to be the same pixel surface component from the pixel surface object? Or can it be a new one?
     
  44. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hmm, I'm not sure what you mean. It has to be a reference to whatever PixelSurface you want to check the pixels of. Probably, you only have one (though you can certainly use more if you need them).
     
  45. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    Oh ok, gotcha. Thank you
     
  46. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    To kind of come back to that question, is there anyway I can reference the pixel surface from a bomb prefab? it doesn't seem to let me unless I make the bomb as a child of the pixel surface game object
     
  47. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    No, you can reference a PixelSurface component just like referencing any other components in Unity. But if you're new to Unity, your options may not be obvious.

    I couldn't find a tutorial about it, but this SO question looks like a decent place to start. Or if you can describe your situation in more detail, we can make a more specific recommendation.

    Trying to pin things down: you reference another object from a script, not from a prefab (though the script in question might happen to be on a prefab). If the script is on a prefab, then it can only directly (i.e., in the Inspector) reference other prefabs, not things in the project hierarchy. That's because prefabs don't live in the scene hierarchy; they live in the project, and could be used in many different scenes (or none of them). So it wouldn't make sense for a prefab to reference something in the scene.

    So if this is your situation, you'll need to find another way to get a reference to the PixelSurface in your code, for example by using FindObjectOfType. Or, you don't make your bomb a prefab at all; make it an object in the scene, and then it can reference other things in the scene. You can still call Instantiate on it to clone it as needed.

    If none of this helps, then please describe what you're trying to do in as much detail as you can, and we'll help!
     
  48. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    That helps a lot, I tried putting the bomb in the scene but whenever I tried to instantiate a clone, it wouldn't work. Do I have to change the code of instantiating it at all if I move it to the scene? Thanks a ton.
     
  49. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    No, instantiating is the same whether you're cloning a prefab in the project or an object in the scene. Perhaps you just need to update the reference on whatever script is doing the cloning?
     
  50. camoPACman

    camoPACman

    Joined:
    Oct 26, 2018
    Posts:
    10
    Perhaps, I’ll try it. That would save be a ton of time and work.