Search Unity

Would a C# BASIC style API make prototyping and game development faster in Unity?

Discussion in 'General Discussion' started by Arowx, Jun 26, 2022.

  1. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    If you look into retro computers that had built in BASIC programming languages you will find that old magazines with code listings of about a page or two in length that (if they worked) would run as a game on that platform.

    Example ZX81 game called BONKERs in
    Code (Boo):
    1.   10 REM **BONKERS**SLR/1985****
    2.   20 LET S=0
    3.   30 PRINT ,,,,"  ² ·  õµ õö ²ö ²óô·öó  ±² ±² ±² ²² öµ  µ    ²³  ³ñ²³ô  ´÷ ´÷ ´÷ õµõõö õ  õõ õò õõ   µ  òñ òñ òñ  ó    õµ õµ ÷¶ õµ ¶³ö  µõ µõ µõ"
    4.   40 PRINT "¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸  BY:  S T E V E N  R E I D **  ¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸"
    5.   50 PRINT ,,,,"STOP THE O""S BEFOE THEY BONK    YOU**"
    6.   60 PRINT ,,,," USE ARROW KEYS FOR MOVEMENT...."
    7.   70 PRINT ,,,,"PRESS ENTER TO BEGIN GAME PLAY??"
    8.   80 PAUSE 4E4
    9.   85 CLS
    10.   90 LET X=10
    11.  100 LET Y=4
    12.  110 LET X1=X
    13.  120 LET Y1=Y
    14.  130 DIM Z(8)
    15.  140 LET Z(1)=4
    16.  150 LET Z(2)=10
    17.  160 LET Z(3)=4
    18.  170 LET Z(4)=21
    19.  180 LET Z(5)=17
    20.  190 LET Z(6)=10
    21.  200 LET Z(7)=17
    22.  210 LET Z(8)=21
    23.  215 FAST
    24.  220 FOR A=0 TO 21
    25.  240 PRINT AT A,0;" ";TAB 31;" ";AT 0,A;"          ";AT 21,A;"          ";AT 1,1;" ";AT 1,30;" ";AT 20,1;" ";AT 20,30;" "
    26.  250 NEXT A
    27.  260 PRINT AT 19,15;"!!";AT 2,15;"!!";AT 3,5;"!!";TAB 15;"!!";TAB 25;"!!";AT 4,5;"!!";TAB 25;"!!";AT 5,5;"!!";TAB 25;"!!"; AT 6,5;"!!";TAB 25;"!!";AT 9,15;"!!";AT 10,1;"!!";TAB 14;"!!!!";TAB29;"!!";AT 11,1;"!!";TAB 14;"!!!!";TAB 29;"!!"; AT 12,15;"!!";AT 15,5;"!!";TAB 25;"!!";AT 16,5;"!!";TAB 25;"!!";AT 17,5;"!!";TAB 25;"!!";AT 18,5;"!!";TAB 15;"!!";TAB 25;"!!"
    28.  270 LET C$="PEEK (PEEK 16398+256*PEEK 16399)"
    29.  275 SLOW
    30.  280 PRINT AT X,Y;"X";AT X1,Y1;"-" AND (X1<>X OR Y1<>Y)
    31.  290 LET X1=X
    32.  300 LET Y1=Y
    33.  310 LET X=X-(INKEY$="7")+(INKEY$="6")
    34.  320 LET Y=Y-(INKEY$="5")+(INKEY$="8")
    35.  325 FAST
    36.  330 FOR C=1 TO 7 STEP 2
    37.  335 IF Z(C)=0 THEN GOTO 400
    38.  340 PRINT AT Z(C),Z(C+1);"O"
    39.  350 IF RND>.5 THEN LET Z(C)=Z(C)+SGN (X-Z(C))
    40.  360 LET Z(C+1)=Z(C+1)+(INT (RND*3-1))
    41.  370 PRINT AT Z(C),Z(C+1);
    42.  380 IF VAL C$=128 OR VAL C$=22 THEN LET Z(C)=0
    43.  390 PRINT "O"
    44.  400 NEXT C
    45.  405 SLOW
    46.  410 PRINT AT X,Y;
    47.  420 IF VAL C$<>0 AND VAL C$<>189 THEN GOTO 600
    48.  430 LET S=S+1
    49.  440 IF Z(1)=0 AND Z(3)=0 AND Z(5)=0 AND Z(7)=0 THEN GOTO 460
    50.  450 GOTO 280
    51.  460 CLS
    52.  465 SLOW
    53.  470 PRINT "YOU COMPLETED A LEVEL...BONUS   POINTS****",,"SCORE:";
    54.  480 LET S=S+400+INT (RND*100+100)
    55.  490 PRINT S,,"PRESS ENTER TO CONTINUE ON******"
    56.  500 PAUSE 4E4
    57.  510 CLS
    58.  520 GOTO 90
    59.  600 CLS
    60.  605 SLOW
    61.  610 PRINT ,,,,"  ² ·  õµ õö ²ö ²óô·öó  ±² ±² ±² ²² öµ  µ    ²³  ³ñ²³ô  ´÷ ´÷ ´÷ õµõõö õ  õõ õò õõ   µ  òñ òñ òñ  ó    õµ õµ ÷¶ õµ ¶³ö  µõ µõ µõ"
    62.  620 PRINT "¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸  BY:  S T E V E N  R E I D **  ¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸"
    63.  630 PRINT ,,,,"   ****YOU'VE BEEN BONKED****   "
    64.  640 PRINT ,,"   YOU SCORED                   "
    65.  650 LET S=S+(100 AND Z(1)=0)+(100 AND Z(3)=0)+(100 AND Z(5)=0)+(100 AND Z(7)=0)
    66.  660 LET C$=STR$ S
    67.  670 FOR F=1 TO LEN C$
    68.  680 LET C$(F)=CHR$ (CODE C$(F)+128)
    69.  690 NEXT F
    70.  700 PRINT AT 13,15;C$;" POINTS**"
    71.  710 PRINT ,,,,"PRESS ENTER TO PLAY GAME AGAIN.."
    72.  720 PAUSE 4E4
    73.  730 IF INKEY$="LPRINT " THEN GOTO 800
    74.  740 CLS
    75.  750 RUN
    76.  800 CLEAR
    77.  810 CLS
    78.  820 SAVE "BO"
    79.  830 RUN
    An entire game UI and instructions in 79 lines of code.

    In theory a good to great C# Unity Basic API should let you make the same game in less lines of code.

    The case for a C# Unity Basic (CUB) API:
    Pros:
    • Going back to a more minimal functional style of programming with hardly any boiler plate code.
    • Reducing the hidden coupling between the Editor Scene and Code.
    • Reduced complexity.
    • Super fast to develop and work on.
    • Extensible and open.
    • Easier to learn.
    • Could allow super small Unity games.
    Cons:
    • Yet another API architectural style to add to the Component OOP, DOTS, Visual, Shader styles already used within Unity.
    • Translation from CUB to Unity C# could be complex.
    • Would CUB code integrate well with standard Unity C# or DOTS.
    • Fast and hacky CUB code could take over and make a mess of Unity standards.
    What would also be interesting is if a CUB API could give developers easier and faster access to higher level features like DOTS, Networking and Threading.

    Do you think a more BASIC game focused API could or should be added to Unity and why/why not?

    PS Programming Challenge - Convert the above bonkers* game to Unity in the fewest lines of code you can?
    * or any retro basic code listed game you can find.
     
    Last edited: Jun 26, 2022
  2. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,700
    Honestly, prototyping speed is not dependent on the amount of code lines. After all we can type fast and copy'n paste even faster. What matters is how intuitive the whole workflow is. Unity does do a decent job at that if you ask me.

    What could actually speed up prototyping, is a more extensive gizmos library that works in gameplay mode.
    Coming from GameMaker many years ago as a teen, I was at first really confused\frustrated that there was no equivalent for
    DrawSprite(sprite, x, y, z, Color, angle)
    - a method allowing you to simply render for one frame from anywhere and stuff like that.
    In Unity, a fair number of API calls are dependent on which method\event they are being called in and visuals are a hassle to use from code (surely because they are a magnitude more complex than what GameMaker had back then).

    GM is quite good for your challenge btw. xP


    However you can get the freedom at slight cost of performance (naturally) with 3rd party libraries in Unity. I did build myself a global sprite- and also mesh- and line-renderer system at one point to use for debugging. Would be really nice to have something like that in the standard library. Partially for fast prototyping, partially for debugging and simply reduce the times you have to switch from code to the editor.

    However no full on extra API that covers the same functionality. For game logic, the stuff Unity already has, is sufficient.
    Furthermore nowadays standards for a "prototype" have grown significantly. You will quickly outgrow such one-script stuff.

    Btw. how about Project Tiny? Never worked with that, but wasn't fast prototyping one of its goal?
    Hopefully they revive that once Dots has progressed.
     
    Last edited: Jun 26, 2022
    JoNax97 and stain2319 like this.
  3. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Good point early on Tiny was it's own little micro game engine based on an ECS approach, unencumbered by Unity baggage.

    Unfortunately Tiny seems to have dropped into Limbo which is odd as DOTS is moving forward?

    That's the idea, a more BASIC functional subset of Unity quick and easy to learn code based functions that let programmers throw ideas around without needing lots of setup complexity.

    Take easy to code Music in Unity, it's not there, but a lot of BASIC programming languages let you play tunes with a few lines of code MSX Basic even had it's own string based Music system.

    And the funny thing is when you look at optimisation and performance the Unity Transform/GameObject paradigm is terribly slow (hence DOTS and Instancing API gfx calls) so a BASIC API could let Unity use DOTS under the hood for amazing performance.
     
  4. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,700
    Not sure where, but I recall some semi official information that it has been put on halt at least until the final architecture of DOTS is established. Since Project Tiny started, they made that paradigm shift to focus on a hybridized approach of DOTS alongside game objects after all.

    We can dream, but honestly if they attempted that, it would not be the first and neither the last project out there that tries to bring "max performance" AND "max convenience\easy-learning" but fails at both goals.
    Performance and simplicity are very often contradicting because performance requires low level calls. Hence why using DOTS and making meaningful use of the Instanced calls is not super trivial.
     
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,989
    This code is a mess, a maintenance nightmare and the kind of code you don‘t want inherit from the team member who just quit. What the heck is „PAUSE 4E4“??

    I‘d rather code this in 790 lines of well laid out and modular C# code. ;)

    Btw, there‘s Visual Scripting, Node Canvas, and others if you really want to simplify/dumbify coding.
     
  6. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,195
    In practice the people who decide to learn the simpler approach will just keep using it. I should know because I did exactly that with QuickBASIC back in the day. When I finally decided to move forward I discovered that I had been using it as a crutch and that I needed to unlearn a great deal of what I had picked up.

    Enterprise developers run into a similar problem but in a different way. For business software it makes sense to use tools like LINQ which greatly reduces the amount of code needed and thus the complexity of debugging, but when you start developing games which are timing critical you discover it's unusable in almost every case.

    Writing fewer lines of code doesn't necessarily mean you've written a better program.
     
    Last edited: Jun 26, 2022
    zombiegorilla, kaiyum and CodeSmile like this.
  7. Murgilod

    Murgilod

    Joined:
    Nov 12, 2013
    Posts:
    10,158
    See, this is a REALLY good point, and I'm saying this who started out with BASIC and similar languages, even using related gamedev simplification stuff that drew from it like DarkBASIC. I think it is extremely important to point this out:

    C# is easier than BASIC. I know that sounds incredibly counterintuitive, but people often confuse "simple" and "easy." BASIC is simple. It's not terribly robust, it reads incredibly inefficiently, and it still does fundamentally require learning all new kinds of syntax. Simple things like its GOTO loop structures make code maintenance and reading an absolute nightmare.

    At its most basic levels, if you want to move a cube in Unity you can do that by writing a single line of code.
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CubeMover : MonoBehaviour
    6. {
    7.     // Start is called before the first frame update
    8.     void Start()
    9.     {
    10.         transform.position = new Vector3(4f, 2f, 0f);
    11.     }
    12.  
    13.     // Update is called once per frame
    14.     void Update()
    15.     {
    16.        
    17.     }
    18. }
    It may seem daunting at first but it's important to take a few things into consideration
    1. The engine already did some of the work for you. You don't have to type all that every time
    2. The code is easily read, being placed line by line and executing in sequential order within designated spaces
    3. The two functions on display have names and descriptions
    So, getting started, at the very beginner level, I already have an advantage. I want the cube to move exactly once. This is a simple thing I can do to start learning. So I watch a youtube tutorial or read the docs or visit Unity Learn and I find out that objects have something called a transform, that stores the position data, and that requires 3 values. Some of this I likely already knew because I'm not coming into this completely blind to the idea of how games work. I have probably played games and wanted to learn how to make them, so I looked a little carefully at games.

    The code also tells me what Start and Update do. Start does things once, at the start. So I can put the code in there. Using very simple syntax, I get the position from the transform and set it to a new position. Thanks to me having taken a grade 9 math class, I know that a vector represents a point with a given position in # dimensional space.

    This is not necessarily completely accurate, there will always be people who go "I WANNA MAKE A GAME," look things up, and then go "TOO HARD" and give up. But let me ask you a hypothetical question to address this:

    Should we also make paints more simple rather than teaching people how to paint?

     
  8. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    But that's 17 lines of boilerplate for one line of code. :p

    Have you seen the new style of C# where you start with an empty text editor and then write your main() function in it as the boilerplate and import are generated?

    Code (CSharp):
    1. // See https://aka.ms/new-console-template for more information
    2. Console.WriteLine("Hello, World!");
    What if Unity could could let you start your game from such a simple template?
     
  9. Murgilod

    Murgilod

    Joined:
    Nov 12, 2013
    Posts:
    10,158
    Except they don't have to and shouldn't. That boilerplate code serves a purpose.

    You are trying to solve a problem that doesn't exist and, in typical arowx fashion, you are dramatically overengineering the solution.
     
    stain2319 and JoNax97 like this.
  10. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,195
    Boilerplate that you don't have to type.
     
    Neonlyte likes this.
  11. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    The boiler plate is a ui and ide problem, not a c# and language problem, you can probably code an interface in a week that just "compile" to raw c# code by taking input from a windows and concatenated it to the boiler plate. In fact a lot of sandbox online coding work like that, half way through. Old basic advantage was the prompt style interface with direct feedback when there was syntax error.

    Regarding making faster, code is less a concern than architecture organizations. Game are programs structured around exception due to gameplay. System get coupled real fast, and there is no easy way to visualize it intuitively for a newcomer. Let say you make and design all your features on their own sandbox, how do you put them together?

    Let say you have this grandiose idea of ai being persistent and able to travel the big open world you made, but now you have to make it work with the streaming system and it's breaking at every seam because now your ai must rely on the terrain but there is no way to have the whole terrain loaded without taking the performance. Worse there is a latency to obtain data due to the streaming process, so there is no guarantee data you query exist when you do. On top of that the animation driven by the ai lost it state because you didn't account for the saving system and now it look like the ai isn't behaving properly and you think it's a bug of the ai and fail to see the animation system isn't set properly. All while the code is perfectly syntactically correct and toss no error, perfectly modular and well commented and as simple and readable as you can get.

    Making this part of coding easier is the real problem. Ie surfacing architecture in some way.
     
    MadeFromPolygons, JoNax97 and Ryiah like this.
  12. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,276
    That code is simple because the result is incredibly simple, you just need a retro simplified graphics/input API, not a change of syntax.
    If you want such an environment, while keeping in Unity, try an asset like https://assetstore.unity.com/packages/templates/systems/retroblit-102064 which goes back to the (more enjoyable?) simple days of a code-only approach.
     
    pekdata and Arowx like this.
  13. pekdata

    pekdata

    Joined:
    Mar 16, 2019
    Posts:
    114
    BASIC style game engines used to be pretty popular. I noticed back in the day that people using simple engines with something like BASIC would often get a lot of games done and released while many others using more advanced languages like C++ would struggle to release their first game. I'm not saying it's all because of the language, might be more about the scope. However, I found it interesting from the 'getting things done' perspective.
     
    Arowx likes this.
  14. Wow, another nonsense Arowx thread. ZX Spectrum as the basis of comparing any development in modern computing with highly complex graphics and all?

    It is beyond ridiculous.
     
  15. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,982
    What exactly is there to gain from comparing ZX spectrum to modern unity?
     
    angrypenguin likes this.
  16. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Code (CSharp):
    1. using System.Collections; using System.Collections.Generic; using UnityEngine;
    2. public class Bonkers : MonoBehaviour {  
    3.     public GameObject startupScreen; public GameObject gameOverScreen; public Vector2 player;
    4.     public Vector2[] os; public Vector3 topLeft = new Vector3(-13, -10, 0f);  
    5.     public const int width = 27; public const int height = 20;
    6.     public const float halfWidth = width / 2; public const float halfHeight = height / 2;
    7.     public float speed = 0.2f; private float nextMoveTime = 0f;
    8.     private int score = 0;
    9.     private void Awake() { Application.targetFrameRate = 60;}
    10.     void Start() { Setup(); }
    11.     void Setup() {
    12.         gameMode = EGameMode.eStarting;
    13.         startupScreen.SetActive(true);
    14.         Basic.CLS();          
    15.         Basic.PrintBlockRegion(0, 0, 14, 1); Basic.PrintBlockRegion(0, 1, 1, 11); Basic.PrintBlockAt(1, 1); Basic.PrintBlockRegion(12, 2, 3, 3); Basic.PrintBlockRegion(11, 9, 5, 3); Basic.PrintBlockRegion(5, 3, 2, 3);
    16.         Basic.PrintBlockRegion(1, 9, 2, 2);      
    17.         os = new Vector2[4];
    18.         os[0] = new Vector2(8, 6); os[1] = new Vector2(8, 13); os[2] = new Vector2(18, 6); os[3] = new Vector2(18, 13);
    19.         foreach (Vector2 o in os) Basic.Print((int)o.x, (int)o.y, 'O');
    20.         player = new Vector2(3, 11);
    21.         Basic.Print(player, 'X'); }
    22.     public enum EGameMode { eStarting, ePlaying, eGameOver };
    23.     public EGameMode gameMode = EGameMode.eStarting;
    24.     void Update() {
    25.         if (gameMode == EGameMode.eStarting) {
    26.             if (Input.GetKeyDown(KeyCode.Return)) {
    27.                 startupScreen.SetActive(false);
    28.                 gameMode = EGameMode.ePlaying;
    29.                 nextMoveTime = Time.realtimeSinceStartup + nextMoveTime;
    30.             } }
    31.         if (gameMode == EGameMode.ePlaying) {
    32.             float t = Time.realtimeSinceStartup;
    33.             if (t >= nextMoveTime) {
    34.                 nextMoveTime = t + speed;
    35.                 int osleft = 0;
    36.                 for (int io = 0; io < os.Length; io++) {
    37.                     if (os[io] != new Vector2(-1, -1)) {
    38.                         Basic.Print(os[io], 'o');
    39.                         Vector2 omove = new Vector2(Random.Range(0, 3) - 1, Random.Range(0, 3) - 1) + os[io];
    40.                         char m = Basic.Peek(omove);
    41.                         if (m != 'B' && m != 'O') os[io] = omove;
    42.                         if (m == 'X' || m == 'x') os[io] = new Vector2(-1, -1);
    43.                         Basic.Print(os[io], 'O');
    44.                         osleft++; } }
    45.                 if (osleft == 0) GameOver();
    46.                 Basic.Print(player, 'x');
    47.                 Vector2 move = player + Basic.PlayerInput();
    48.                 char pm = Basic.Peek(move);
    49.                 if (pm == 'x' || pm == ' ') player = move;
    50.                 if (pm == 'O' || pm == 'o') GameOver();
    51.                 Basic.Print(player, 'X'); } }
    52.         if (gameMode == EGameMode.eGameOver) {
    53.             if (Input.GetKeyDown(KeyCode.Return)) {
    54.                 gameOverScreen.SetActive(false);              
    55.                 Setup(); } } }  
    56.     void GameOver() {
    57.         gameOverScreen.SetActive(true);
    58.         gameMode = EGameMode.eGameOver; }        
    59. }
    59 lines of code I though it would be possible to write a Basic style game in less code with Unity.

    Could probably shave off another 12 if I concatenate more lines but don't want to totally kill readability.

    Mind you I did have to write a Print command and build a display buffer based on a char array that renders meshes using the Graphics commands.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Basic : MonoBehaviour
    6. {
    7.     public static Basic use;
    8.  
    9.     //Grid based display
    10.     public const int width = 27;
    11.     public const int height = 20;
    12.  
    13.     public const float offsetx = width / 2f;
    14.     public const float offsety = height / 2f - 0.5f;
    15.  
    16.     public char[,] display = new char[width, height];
    17.     private Dictionary<char, Sprite> spriteDictonary;
    18.  
    19.     public Sprite[] sprites;
    20.  
    21.     public bool mirrorX = true;
    22.     public bool mirrorY = true;
    23.  
    24.     [System.Serializable]
    25.     public struct Sprite
    26.     {
    27.         public Mesh mesh;
    28.         public Material material;
    29.         public char c;
    30.     }
    31.  
    32.     void Awake()
    33.     {
    34.         use = this;
    35.      
    36.         spriteDictonary = new Dictionary<char, Sprite>(sprites.Length);
    37.  
    38.         CLS();
    39.  
    40.         foreach (Sprite sprite in sprites)
    41.         {
    42.             spriteDictonary.Add(sprite.c, sprite);
    43.         }
    44.     }
    45.  
    46.     public static void CLS()
    47.     {
    48.         for (int x = 0; x < width; x++)
    49.         {
    50.             for (int y = 0; y < height; y++)
    51.             {
    52.                use.display[x, y] = ' ';
    53.             }
    54.         }
    55.     }
    56.  
    57.     public static char Peek(int x, int y)
    58.     {
    59.         if (x < 0 || x >= width || y < 0 || y >= height)
    60.         {
    61.             Debug.Log("Peek " + x + "," + y + " out of range");
    62.             return '!';
    63.         }
    64.         return use.display[x, y];
    65.     }
    66.  
    67.     public static char Peek(Vector2 pos) { return Peek((int)pos.x, (int)pos.y); }
    68.  
    69.     public static void Print(int x, int y, char c)
    70.     {
    71.         if (x < 0)
    72.         {
    73.             x += width;
    74.         }
    75.  
    76.         if (x >= width)
    77.         {
    78.             x = x % width;
    79.             y = y + x / width;
    80.         }
    81.  
    82.         if (y < 0)
    83.         {
    84.             y += height;
    85.         }
    86.  
    87.         if (y >= height)
    88.         {
    89.             y = y % height;
    90.         }
    91.  
    92.         use.display[x, y] = c;
    93.     }
    94.  
    95.     public void Print(int x, int y, string s)
    96.     {
    97.         foreach (char c in s)
    98.         {
    99.             Print(x, y, c);
    100.             x++;
    101.             if (x > width)
    102.             {
    103.                 x = 0;
    104.                 y++;
    105.             }
    106.         }
    107.     }
    108.  
    109.     void Update()
    110.     {
    111.         Vector3 position = Vector3.zero;
    112.         Quaternion rotation = Quaternion.identity;
    113.  
    114.         int layer = 0; //default layer
    115.  
    116.         for (int x = 0; x < width; x++)
    117.         {
    118.             position.x = x - offsetx;
    119.  
    120.             for (int y = 0; y < height; y++)
    121.             {
    122.                 position.y = y - offsety;
    123.  
    124.                 char c = display[x, y];
    125.  
    126.                 Sprite sprite;
    127.  
    128.                 if (spriteDictonary.TryGetValue(c, out sprite))
    129.                 {                  
    130.                     Graphics.DrawMesh(sprite.mesh, position, rotation, sprite.material, layer);
    131.                 }  
    132.                 else if (c != ' ') Debug.Log("Sprite Key[" + c + "] not found in dictonary");              
    133.             }
    134.         }
    135.     }
    136.  
    137.     public static Vector2 PlayerInput()
    138.     {
    139.         float h = Input.GetAxis("Horizontal");
    140.         if (h > 0f) h = 1f;
    141.         if (h < 0f) h = -1f;
    142.         float v = Input.GetAxis("Vertical");
    143.         if (v > 0f) v = 1f;
    144.         if (v < 0f) v = -1f;
    145.         return new Vector2(h, v);
    146.     }
    147.  
    148.     public static void PrintBlockRegion(float x, float y, float w, float h)
    149.     {
    150.         for (int xw = 0; xw < w; xw++)
    151.             for (int yw = 0; yw < h; yw++)
    152.                 PrintBlockAt(x + xw, y + yw);
    153.     }
    154.     public static void PrintBlockAt(Vector2 pos, bool mirror = false)
    155.     {
    156.         if (mirror == false)
    157.         {
    158.             if (use.mirrorX) PrintBlockAt(width - pos.x - 1, pos.y, true);
    159.             if (use.mirrorY) PrintBlockAt(pos.x, height - pos.y - 1, true);
    160.             if (use.mirrorX && use.mirrorY) PrintBlockAt(width - pos.x - 1, height - pos.y - 1, true);
    161.         }
    162.         Print((int)(pos.x), (int)(pos.y), 'B');
    163.     }
    164.     public static void PrintBlockAt(float x, float y, bool mirror = false) { PrintBlockAt(new Vector2(x, y), mirror); }
    165.     public static void Print(Vector2 pos, char c) { Print((int)pos.x, (int)pos.y, c); }
    166.     public static void Print(float x, float y, char c) { Print((int)x, (int)y, c); }
    167. }
    168.  

    Although this could be fun as all those old school retro listings could be converted using a basic framework like this.
     
  17. Murgilod

    Murgilod

    Joined:
    Nov 12, 2013
    Posts:
    10,158
    Again, you're not getting it. The "amount of code" is not the problem and your "basic style" makes things worse because it sacrifices readability and maintainability in the name of potentially having to type slightly less. The amount of effort it takes to write code from a text entry perspective is nothing, it's a non-issue you're presenting as a big issue.
     
  18. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    If you want less code, just use a library, or make your own ... that's what function and object declaration are for. That is what basic is really, a wrapper for a bunch of low level code.
     
  19. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    And also scalability and flexibility.
    --
    While that kind code might be cute for STEM "my first code" project, it's not useful outside very narrow needs.

    It might be helpful to study computer science or history of languages. There reasons things work the way they do, not because no one ever thought of sticking basic into a modern, advanced game engine. Honestly you are asking questions that have long been sorted out.
     
  20. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Code (CSharp):
    1. using System.Collections; using System.Collections.Generic; using UnityEngine; using static Basic;
    2. public class Bonkers : MonoBehaviour {  
    3.     public Veg player; public Veg[] os; private int score = 0;  
    4.     void Start() {  use.setupGameState = Setup;
    5.                     Setup(); }
    6.     void Setup() {  SetupGame();
    7.                     PrintBlocks(new Region(0, 0, 14, 1), new Region(0, 1, 1, 11), new Region(12, 2, 3, 3), new Region(11, 9, 5, 3), new Region(5, 3, 2, 3), new Region(1, 9, 2, 2));
    8.                     PrintBlockAt(1, 1);
    9.                     os = new Veg[4];
    10.                     os[0] = new Veg(8, 6, 'O'); os[1] = new Veg(8, 13, 'O'); os[2] = new Veg(18, 6, 'O'); os[3] = new Veg(18, 13, 'O');
    11.                     player = new Veg(3, 11, 'X'); }
    12.     void Update() { if (Playing()) {
    13.                         int osleft = 0;
    14.                         for (int io = 0; io < os.Length; io++) {
    15.                             if (os[io].active) {
    16.                                 Print(os[io], 'o');
    17.                                 Vector2Int omove = new Vector2Int(RND(0, 3) - 1, RND(0, 3) - 1) + os[io].xy;
    18.                                 char m = Peek(omove);
    19.                                 if (m != 'B' && m != 'O') os[io].xy = omove;
    20.                                 if (m == 'X' || m == 'x') os[io].active = false;
    21.                                 osleft++; } }
    22.                         if (osleft == 0) GameOver();
    23.                         Print(player, 'x');
    24.                         Vector2Int move = player.xy + PlayerInput();
    25.                         char pm = Peek(move);
    26.                         if (pm == 'x' || pm == ' ') player.xy = move;
    27.                         if (pm == 'O' || pm == 'o') GameOver(); } } }
    27 lines of code but larger Basic API.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Rnd = UnityEngine.Random;
    5. public class Basic : MonoBehaviour
    6. {
    7.     public static Basic use;
    8.  
    9.     //Grid based display
    10.     public const int width = 27;
    11.     public const int height = 20;
    12.  
    13.     public const float offsetx = width / 2f;
    14.     public const float offsety = height / 2f - 0.5f;
    15.  
    16.     public char[,] display = new char[width, height];
    17.     private Dictionary<char, Sprite> spriteDictonary;
    18.  
    19.     public Sprite[] sprites;
    20.  
    21.     public bool mirrorX = true;
    22.     public bool mirrorY = true;
    23.  
    24.     public delegate void SetupGameState();
    25.     public SetupGameState setupGameState;
    26.  
    27.     [System.Serializable]
    28.     public struct Sprite
    29.     {
    30.         public Mesh mesh;
    31.         public Material material;
    32.         public char c;
    33.     }
    34.  
    35.     public static List<Veg> Vegs = new List<Veg>();
    36.  
    37.     public class Veg // Vector game element
    38.     {
    39.         public Vector2Int xy;      
    40.         public bool active;
    41.         public char c;
    42.  
    43.         public Veg(int x, int y, char ch, bool a = true)
    44.         {
    45.             xy = new Vector2Int(x, y);
    46.             c = ch;
    47.             active = a;
    48.  
    49.             Vegs.Add(this);
    50.         }
    51.     }
    52.  
    53.     public enum EGameMode { eStarting, ePlaying, eGameOver };
    54.     public static EGameMode gameMode;
    55.  
    56.     public GameObject startupScreen;
    57.     public GameObject gameOverScreen;
    58.  
    59.     public static void SetupGame()
    60.     {
    61.         gameMode = EGameMode.eStarting;
    62.         use.startupScreen.SetActive(true);
    63.         use.gameOverScreen.SetActive(false);
    64.         CLS();
    65.     }
    66.  
    67.  
    68.     void Awake()
    69.     {
    70.         Application.targetFrameRate = 60;
    71.  
    72.         use = this;
    73.      
    74.         spriteDictonary = new Dictionary<char, Sprite>(sprites.Length);      
    75.  
    76.         foreach (Sprite sprite in sprites)
    77.         {
    78.             spriteDictonary.Add(sprite.c, sprite);
    79.         }
    80.  
    81.         SetupGame();
    82.     }
    83.  
    84.     public static void CLS()
    85.     {
    86.         for (int x = 0; x < width; x++)
    87.         {
    88.             for (int y = 0; y < height; y++)
    89.             {
    90.                use.display[x, y] = ' ';
    91.             }
    92.         }
    93.     }
    94.  
    95.     public static char Peek(int x, int y)
    96.     {
    97.         if (x < 0 || x >= width || y < 0 || y >= height)
    98.         {
    99.             Debug.Log("Peek " + x + "," + y + " out of range");
    100.             return '!';
    101.         }
    102.         return use.display[x, y];
    103.     }
    104.  
    105.     public static char Peek(Vector2 pos) { return Peek((int)pos.x, (int)pos.y); }
    106.  
    107.     public static void Print(int x, int y, char c)
    108.     {
    109.         if (x < 0) x += width;    
    110.  
    111.         if (x >= width)
    112.         {
    113.             x = x % width;
    114.             y = y + x / width;
    115.         }
    116.  
    117.         if (y < 0) y += height;    
    118.  
    119.         if (y >= height) y = y % height;      
    120.  
    121.         use.display[x, y] = c;
    122.     }
    123.  
    124.     public void Print(int x, int y, string s)
    125.     {
    126.         foreach (char c in s)
    127.         {
    128.             Print(x, y, c);
    129.             x++;
    130.             if (x > width) {
    131.                 x = 0;
    132.                 y++;
    133.             }
    134.         }
    135.     }
    136.  
    137.     public static float speed = 0.2f;
    138.     private static float nextMoveTime = 0f;
    139.  
    140.     public static bool Playing()
    141.     {
    142.         if (gameMode == EGameMode.ePlaying)
    143.         {
    144.             float t = Time.realtimeSinceStartup;
    145.             if (t >= nextMoveTime)
    146.             {
    147.                 nextMoveTime = t + speed;
    148.                 return true;
    149.             }
    150.         }
    151.         return false;
    152.     }
    153.  
    154.  
    155.     void Update()
    156.     {
    157.         if (gameMode == EGameMode.eStarting && InputKey(KeyCode.Return))
    158.         {
    159.             startupScreen.SetActive(false);
    160.             gameMode = EGameMode.ePlaying;
    161.             nextMoveTime = Time.realtimeSinceStartup + nextMoveTime;
    162.         }      
    163.  
    164.         if (gameMode == EGameMode.eGameOver && InputKey(KeyCode.Return)) setupGameState();
    165.     }
    166.  
    167.     private void LateUpdate()
    168.     {          
    169.         // Display all enemies
    170.         foreach (Veg v in Vegs)
    171.         {
    172.             if (v.active) Print(v);
    173.         }
    174.  
    175.         Vector3 position = Vector3.zero;
    176.         Quaternion rotation = Quaternion.identity;
    177.  
    178.         int layer = 0; //default layer
    179.  
    180.         for (int x = 0; x < width; x++)
    181.         {
    182.             position.x = x - offsetx;
    183.  
    184.             for (int y = 0; y < height; y++)
    185.             {
    186.                 position.y = y - offsety;
    187.  
    188.                 char c = display[x, y];
    189.  
    190.                 Sprite sprite;
    191.  
    192.                 if (spriteDictonary.TryGetValue(c, out sprite))
    193.                 {                  
    194.                     Graphics.DrawMesh(sprite.mesh, position, rotation, sprite.material, layer);
    195.                 }  
    196.                 else if (c != ' ') Debug.Log("Sprite Key[" + c + "] not found in dictonary");              
    197.             }
    198.         }
    199.     }
    200.  
    201.     public static void GameOver()
    202.     {
    203.         use.gameOverScreen.SetActive(true);
    204.         gameMode = EGameMode.eGameOver;
    205.     }
    206.  
    207.     public static bool InputKey(KeyCode key)
    208.     {
    209.         return Input.GetKeyDown(key);
    210.     }
    211.  
    212.     public static int RND(int a, int b)
    213.     {
    214.         return Rnd.Range(a, b);
    215.     }
    216.  
    217.     public static Vector2Int PlayerInput()
    218.     {
    219.         float h = Input.GetAxis("Horizontal");
    220.         if (h > 0f) h = 1f;
    221.         if (h < 0f) h = -1f;
    222.         float v = Input.GetAxis("Vertical");
    223.         if (v > 0f) v = 1f;
    224.         if (v < 0f) v = -1f;
    225.         return new Vector2Int((int)h, (int)v);
    226.     }
    227.  
    228.     public struct Region {
    229.         public int x;
    230.         public int y;
    231.         public int w;
    232.         public int h;
    233.         public Region(int rx, int ry, int rw, int rh)
    234.         {
    235.             x = rx;
    236.             y = ry;
    237.             w = rw;
    238.             h = rh;
    239.         }
    240.     }
    241.  
    242.     public static void PrintBlocks(params Region[] regions)
    243.     {
    244.         foreach (Region r in regions) PrintBlocks(r);
    245.     }
    246.  
    247.     public static void PrintBlocks(Region region)
    248.     {
    249.         PrintBlocks(region.x, region.y, region.w, region.h);
    250.     }
    251.  
    252.     public static void PrintBlocks(float x, float y, float w, float h)
    253.     {
    254.         for (int xw = 0; xw < w; xw++)
    255.             for (int yw = 0; yw < h; yw++)
    256.                 PrintBlockAt(x + xw, y + yw);
    257.     }
    258.     public static void PrintBlockAt(Vector2 pos, bool mirror = false)
    259.     {
    260.         if (mirror == false)
    261.         {
    262.             if (use.mirrorX) PrintBlockAt(width - pos.x - 1, pos.y, true);
    263.             if (use.mirrorY) PrintBlockAt(pos.x, height - pos.y - 1, true);
    264.             if (use.mirrorX && use.mirrorY) PrintBlockAt(width - pos.x - 1, height - pos.y - 1, true);
    265.         }
    266.         Print((int)(pos.x), (int)(pos.y), 'B');
    267.     }
    268.     public static void PrintBlockAt(float x, float y, bool mirror = false) { PrintBlockAt(new Vector2(x, y), mirror); }
    269.     public static void Print(Vector2 pos, char c) { Print((int)pos.x, (int)pos.y, c); }
    270.     public static void Print(float x, float y, char c) { Print((int)x, (int)y, c); }
    271.     public static void Print(Veg v, char c = '?') { Print(v.xy.x, v.xy.y, c == '?' ? v.c : c); }
    272. }
     
  21. Murgilod

    Murgilod

    Joined:
    Nov 12, 2013
    Posts:
    10,158
     
  22. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    If you're going to start shoving game state into your so-called system API then you may as well get the whole thing down to:
    Code (csharp):
    1. void Update()
    2. {
    3.     RunGame();
    4. }
    Seriously, though, listen to the others. As in, take a few moments to truly grok what they're saying.

    Modern languages aren't different to BASIC because all of their designers were stupid. BASIC isn't is what it is because its designers were prescient and perfect. They're all products of their times. BASIC is a product of the 1960s, when computers were primitive and monitors were tiny. In today's environment you only make things worse by trying to mimic the styles and approaches used back then.

    In other words, you're putting effort into making things harder by rolling back nearly 60 years' worth of advancement in computer science.

    The answer to your original question is just "no".
     
  23. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    also blitzbasic exist, the perfect gaming basic, don't at me
     
    Arowx likes this.
  24. Also I dare you to write CRT IRQ in C#. In the name of nostalgia.
     
    neoshaman likes this.
  25. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Code (CSharp):
    1. using System.Collections; using System.Collections.Generic; using UnityEngine; using static Basic;
    2. public class Bonkers : MonoBehaviour {  
    3.     public Veg player; public Veg[] os; private int score = 0;  
    4.     void Start() {  use.setupGameState = Setup;
    5.                     SetBlockers('O', "B"); SetBlockers('X', "B"); SetKillers('O', "x"); SetKillers('X', "oO");
    6.                     Setup(); }
    7.     void Setup() {  SetupGame();
    8.                     score = 0;
    9.                     PrintBlocks(new Region(0, 0, 14, 1), new Region(0, 1, 1, 11), new Region(12, 2, 3, 3), new Region(11, 9, 5, 3), new Region(5, 3, 2, 3), new Region(1, 9, 2, 2));
    10.                     PrintBlockAt(1, 1);
    11.                     os = new Veg[4]; os[0] = new Veg(8, 6, 'O'); os[1] = new Veg(8, 13, 'O'); os[2] = new Veg(18, 6, 'O'); os[3] = new Veg(18, 13, 'O');
    12.                     player = new Veg(3, 11, 'X'); }
    13.     void Update() { if (Playing()) {
    14.                         int osleft = 0;
    15.                         score += 1;
    16.                         for (int io = 0; io < os.Length; io++) {
    17.                             if (os[io].active) {
    18.                                 Print(os[io], 'o');
    19.                                 if (RndMove(os[io])) osleft++;
    20.                                 else score += 100; } }
    21.                         if (osleft == 0) GameOver();
    22.                         Print(player, 'x');
    23.                         if (!Move(player, PlayerInput() + player.xy)) GameOver(); } } }
    23 lines with a collision system built into the 159 line Basic API.
     
  26. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,700
    Sounds like you wanna look into "Code Golf". There is an entire community dedicated to that xP
     
    angrypenguin likes this.
  27. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,276
    JoNax97 likes this.
  28. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,982
    Something I have come to realise from this and your threads in general is, you truly are the master of putting your fingers in your ears and going "lalalala I cant hear you" :D
     
    NotaNaN, Ryiah, neoshaman and 2 others like this.
  29. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,779
    The OP thread are never constructive discussion with OP himself.
    At best, constructive discussion is between other members, derived in such threads.
    This is the only reason these threads are not closed immediately. But it is just matter of time (because of pushing nonsense).
    Trying pointing out to OP anything, is like shouting into the wall.
    Yet seems people still keep shouting into the wall ...
     
  30. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    BUT It's the water cooler wall! when you need a distraction to reset your mind after 4hours of tedious rot bug hunts!
     
    MadeFromPolygons likes this.