Monogame - Issue with Loading between levels - c#
As I'm currently making a 2D game, I'm trying to make it possible for my character to load between levels but I'm having a bit of an issue. The first level that my player starts on is loaded in an array with no issue at all in the Game1 class like this:
secondLevel secondLev; // instance for secondLevel class
Map map; // instance for Map class
secondLev = new secondLevel(); // used in the Initialize() function
protected override void LoadContent()
{
map.Generate(new int[,] {
// 0 = no tile drawn
// 3 = tile is drawn
{0,0,0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,0,0,},
{3,3,3,3,3,3,3,3,3,3,},
{3,3,3,3,3,3,3,3,3,3,},
}, 57); // size
}
So to be able to load the second level, I have attempted to make a new class called secondLevel that holds the new level array and simply loads the array in its Load() function, like so:
class secondLevel
{
Map map;
public void Initialize()
{
map = new Map();
}
public void Load()
{
map.Generate(new int[,] {
// 0 = no tile drawn
// 3 = tile is drawn
{0,0,0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,0,0,},
{3,3,3,3,3,3,3,3,3,3,},
{3,3,3,3,3,3,3,3,3,3,},
{3,3,3,3,3,3,3,3,3,3,},
{3,3,3,3,3,3,3,3,3,3,},
}, 57); // size
}
}
Now in my Game1 class, I have placed an if statement that checks if the player has collided with the spike that loads the next level, like so:
if (player.Bounds.Intersects(spike1.Bounds)) // if player intersects with spike
{
secondLev.Load();
}
But when my player intersects with the spike, my game freezes and I get the error message: Object reference not set to an instance of an object.
What is my issue?
If I'm missing any additional code to my question that could help you fix this, please let me know!
Due to lack of context, it is hard to tell what exactly caused it. However, if the exception were to happen within the code that you presented, one of the following might be not be initialized : secondLev or secondLev.map.
if (player.Bounds.Intersects(spike1.Bounds)) // if player intersects with spike
{
secondLev = new secondLevel();
secondLev.Initialize();
secondLev.Load();
}
If that doesn't solve the problem, you should look at the stacktrace for clue.
Your secondLevel class is missing a constructor. That means that the Load() and Initialize() methods are never called. To fix this error, add a constructor that calls those methods.
Example:
public secondLevel(){
this.Initialize();
this.Load();
}
Another thing: you shouldn't be making a whole new class for each new level. Instead, you should have a Level struct (or class if you really want) that has all the properties of a Level. Then, to make new levels, just do Level secondLevel = new Level(map,size);
Related
Serialize the field from Scriptable in another Scriptable
I have found an interesting "issue" in UnityEditor regarding Scriptable objects. Below is simple example: I have scriptable class ScriptableExpressionBulder that holds some list and a float field. Then I have another class ScriptableExpressionCreator that holds a LIST of scriptable objects mentioned previously. The code looks like this: public class ScriptableExpressionBuilder : ScriptableObject { public List<MultipleExpressionBuilder> multipleExpressionBuilder; public float delay; } public class ScriptableExpressionCreator : ScriptableObject { public List<ScriptableExpressionBuilder> List; } When creating ScriptableExpressionBuilder the fields from (MultipleExpressionBuilder) comes up nicely but it also adds a second field "delay" at the end (which is perfect to this point). Now for the interesting part: The second class ScriptableExpressionCreator holds a list of previous scriptables but it doesn't serialize the "delay" field in this current scriptable. The only field that is serialized is a Scriptable class. Is there a way to serialize the field from ScriptableExpressionBuilder in another Scriptable that holds a list of these scriptables so I can basically set the delay from where this expressions are called. I could however populate the field in the inspector of original scriptable, but the wouldnt be reusable since each delay is different.
You had the question deleted but I voted to reopen it since I already had started to write the solution and finished just when you deleted it and I think this now does what you want and gives you a good start point for further extending it where needed ;) That's not really an issue, however, not possible built-in. For ScriptableObject (and anything else that inherits from UnityEngine.Object) the Inspector by default draws an object field. If you want to draw something else you would need a custom editor script. There is not really away around customizing the Inspector in order to expose fiels of other UnityEngine.Object references in the Inspector of another one. It could look somewhat like e.g. (for now assuming you only want the delay field to be exposed additionally) #if UNITY_EDITOR using System; using UnityEditor; using UnityEditorInternal; using UnityEngine; [CustomEditor(typeof(ScriptableExpressionCreator))] public class ScriptableExpressionCreatorEditor : Editor { private SerializedProperty _list; // I know ... but that's your fault for naming th field List :P private ReorderableList _listList; // For Edito this is called when the object gains focus and the Inspector is loaded for this scriptableobject instance private void OnEnable() { // Find and link the serialized field called "List" _list = serializedObject.FindProperty(nameof(ScriptableExpressionCreator.List)); // Initialize and configure the ReorderableList to draw the referenced elements in the way we want _listList = new ReorderableList(serializedObject, _list, true, true, true, true) { // How is te list header drawn? drawHeaderCallback = rect => EditorGUI.LabelField(rect, _list.displayName), // how should each element be drawn? drawElementCallback = (rect, index, active, focused) => { // get the current element var element = _list.GetArrayElementAtIndex(index); // draw the default object reference field with the height of a single line without the label EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); // if no object is referenced do nothing more if (!element.objectReferenceValue) return; // move one line lower rect.y += EditorGUIUtility.singleLineHeight; // get a serializedObject of the reference var elementsSerializedObject = new SerializedObject(element.objectReferenceValue); // same as in our own OnInspectorGUI method below loads the current values elementsSerializedObject.Update(); // for now assuming you only want the delay field var delay = elementsSerializedObject.FindProperty(nameof(ScriptableExpressionBuilder.delay)); // draw the delay field EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), delay); // same as in our own OnInspectorGUI method below writes back changed values and handles undo/redo marking dirty etc elementsSerializedObject.ApplyModifiedProperties(); }, // how do we get the height of one element? elementHeightCallback = index => { // get the current element var element = _list.GetArrayElementAtIndex(index); // if nothing is referenced we only need a single line if (!element.objectReferenceValue) { return EditorGUIUtility.singleLineHeight; } // otherwise we need two lines, one for the object field and one for the delay field return EditorGUIUtility.singleLineHeight * 2; } }; } public override void OnInspectorGUI() { // draw the script field DrawScriptField(); // loads current values into the serialized version serializedObject.Update(); // draw the list according to the settings above _listList.DoLayoutList(); // writes bac any changed values into the actual instance and handles undo/redo marking dirty etc serializedObject.ApplyModifiedProperties(); } // Draws the default script field on the top the Inspector private void DrawScriptField() { // The script field is disabled since nobody is supposed to evr change it EditorGUI.BeginDisabledGroup(true); EditorGUILayout.ObjectField("Script", MonoScript.FromScriptableObject((ScriptableExpressionCreator)target), typeof(ScriptableExpressionCreator), false); EditorGUI.EndDisabledGroup(); // leave a little space between the script field and the actual inspector conten EditorGUILayout.Space(); } } #endif For now this would look like this
Dynamically render GameObject in Unity C#
I am working on a AR project, where the virtual objects will be shown/hide in the scene based on information found in a text file. The text files will be updated from an external service. So I need to read the file on a frequent interval and update the scene. As a result I only have the Camera object and I am rendering the scene in OnPreCull() method. The text files contain many objects but not all the objects are within the scene at any instance of time. I was looking for a way to render only those objects that are within the scene. Will creating and placing the gameobjects in the OnPreCull() method crate any performance issue?
Will creating and placing the gameobjects in the OnPreCull() method crate any performance issue? Yes absolutely ... so would it if you do it in Update or any other repeatedly called method. Instead you should rather Instantiate objects in Awake and only activate or deactivate them. Let's say you have 3 objects A, B and C than I would make a kind of controller class that looks like public class ObjectsController : MonoBehaviour { // Define in which intervals the file should be read/ the scene should be updated public float updateInterval; // Prefabs or simply objects that are already in the Scene public GameObject A; public GameObject B; public GameObject C; /* Etc ... */ // Here you map the names from your textile to according object in the scene private Dictionary<string, GameObject> gameObjects = new Dictionary<string, gameObjects>(); private void Awake () { // if you use Prefabs than instantiate your objects here; otherwise you can skip this step var a = Instantiate(A); /* Etc... */ // Fill the dictionary gameObjects.Add(nameOfAInFile, a); // OR if you use already instantiated references instead gameObjects.Add(nameOfAInFile, A); } } private void Start() { // Start the file reader StartCoroutine (ReadFileRepeatedly()); } // Read file in intervals private IEnumerator ReadFileRepeatedly () { while(true) { //ToDo Here read the file //Maybe even asynchronous? // while(!xy.done) yield return null; // Now it depends how your textile works but you can run through // the dictionary and decide for each object if you want to show or hide it foreach(var kvp in gameObjects) { bool active = someConditionDependingOnTheFile; kvp.value.SetActive(active); // And e.g. position it only if active if (active) { kvp.value.transform.position = positionFromFile; } } // Wait for updateInterval and repeat yield return new WaitForSeconds (updateInterval); } } If you have multiple instances of the same prefab you also should have a look at Object Pooling
I'd recommend adding each of the game objects to a registry and the switching them on or off (dis/enable SetActive) via the registry class's Update() cycle. One Update() process to retrieve and handle the server file, another Update() process to dis/enable objects. Might sound oversimplified however it's the fastest way I think of getting the result. Good Luck!
Xamarin - iOS Multiple polygons on map
I am currently following this tutorial for adding a polygon to a map. I need to be able to add multiple polygons to my map, so I have slightly altered the code so that I can use addOverlays which takes in an array of IMKOverlay objects instead of one addOverlay which just takes in a single IMKOverlay object. This doesn't work however... It only draws the first polygon on the map! void addPolygonsToMap() { overlayList = new List<IMKOverlay>(); for (int i = 0; i < polygons.Count; i++) { CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[polygons[i].Count]; int index=0; foreach (var position in polygons[i]) { coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude); index++; } var blockOverlay = MKPolygon.FromCoordinates(coords); overlayList.Add(blockOverlay); } IMKOverlay[] imko = overlayList.ToArray(); nativeMap.AddOverlays(imko); } In this discussion, it would appear that I have to call a new instance of MKPolygonRenderer each time I need to add another polygon to my map, but I'm unsure how this example translates to my code. Here is my MKPolygonRenderer function: MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper) { if (polygonRenderer == null && !Equals(overlayWrapper, null)) { var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay; polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon) { FillColor = UIColor.Red, StrokeColor = UIColor.Blue, Alpha = 0.4f, LineWidth = 9 }; } return polygonRenderer; }
Create a new renderer instance each time OverlayRenderer is called, there is no need to cache the renderer in a class level variable as the MKMapView will cache the renderers as needed. Subclass MKMapViewDelegate: class MyMapDelegate : MKMapViewDelegate { public override MKOverlayRenderer OverlayRenderer(MKMapView mapView, IMKOverlay overlay) { switch (overlay) { case MKPolygon polygon: var prenderer = new MKPolygonRenderer(polygon) { FillColor = UIColor.Red, StrokeColor = UIColor.Blue, Alpha = 0.4f, LineWidth = 9 }; return prenderer; default: throw new Exception($"Not supported: {overlay.GetType()}"); } } } Instance and assign the delegate to your map: mapDelegate = new MyMapDelegate(); map.Delegate = mapDelegate; Note: Store the instance of your MyMapDelegate in a class level variable as you do not want to get GC'd Update: MKMapView has two steps involved to display an overlay on its map. 1. Calling `AddOverlay` and `AddOverlays` First you add overlays to the map that conform to IMKOverlay. There are basic built-in types such as MKCircle, MKPolygon, etc... but you can also design your own overlays; i.e. overlays that define the location of severe weather (lightning, storm clouds, tornados, etc..). These MKOverlays describe the geo-location of the item but not how to draw it. 2. Responding to `OverlayRenderer` requests When the display area of the map intersects with one of the overlays, the map need to draw it on the screen. The map's delegate (your MKMapViewDelegate subclass) is called to supply a MKOverlayRenderer that defines the drawing routines to paint the overlay on the map. This drawing involves converting the geo-coordinates of the overlay to local display coordinates (helper methods are available) using Core Graphics routines (UIKit can be used with some limitations). There are basic built-in renderers for MKCircleRenderer, MKPolygonRenderer, etc.. that can be used or you can write your own MKOverlayRenderer subclass. You could supply a custom way to renderer a MKCircle overlay, maybe a target-style red/white multi-ringed bullseye, instead of the way the default circle renderer draws it, or custom renderers that draw severe storm symbols within the bounds of a MKPolygon to match your custom severe storm overlays. My Example code: Since you are using MKPolygon to build your overlays, you can use the MKPolygonRenderer to display them. In my example, I provide a pattern matching switch (C# 6) that returns a semi-transparent Red/Blue MKPolygonRenderer for every MKPolygon that you added to the map (if you added a non-MKPolygon based overlay it will throw an exception).
I was also stuck in this issue and I have found the way to create the sub class of MKPolygon. I have checked it with my example and it works like a charm. But not sure that Apple may reject my app or not. public class CvPolyon : MKPolygon { public CustomObject BoundaryOption { get; } public CvPolyon1(MKPolygon polygon, CustomObject boundaryOption) :base(polygon.Handle) { BoundaryOption = boundaryOption; } } We can add polygon on map like this. var polygon = MKPolygon.FromCoordinates(coordinates); var overlay = new CvPolyon(polygon, new CustomObject()); mapView.AddOverlay(overlay); We can recognize our polygon in the class which extends MKMapViewDelegate like this. public override MKOverlayRenderer OverlayRenderer(MKMapView mapView, IMKOverlay overlay) { if (overlay is CvPolyon polygon) { var polygonRenderer = new MKPolygonRenderer(polygon) { FillColor = polygon.BoundaryOption.AreaColor, StrokeColor = polygon.BoundaryOption.LineColor, Alpha = polygon.BoundaryOption.Alpha, LineWidth = polygon.BoundaryOption.LineWidth }; if (polygon.BoundaryOption.IsDashedLine) polygonRenderer.LineDashPattern = new[] { new NSNumber(2), new NSNumber(5) }; return polygonRenderer; } return mapView.RendererForOverlay(overlay); }
Instantiate GameObjects from editor via InitializeOnLoad
I have a programmatically generated board of a certain size. In order to see it, I would normally have to hit run, which is quite annoying and makes it hard to visualize. Is it possible to make, and thus be able to visualize, the board via InitializeOnLoad? I gave it a try: Setup.cs [InitializeOnLoad] public class Setup : MonoBehaviour { public static Map initialMap; static Setup(){ initialMap = new Map (); initialMap.createMap (); } } Map.cs public class Map { private Tile[,] tiles= new Tile[5,5]; //I had Resources.Load here, but apparently thats not allowed either... public GameObject defaultObj= new GameObject("MyCreatedGO"); public Map (){ Debug.Log("In Constructor"); } public void createMap(){ for (int x = 0; x < tiles.GetLength(0); x += 1) { for (int y = 0; y < tiles.GetLength(1); y += 1) { // Tile Instantiates the defaultObj tiles [x, y] = new Tile (defaultObj, new Vector3 (x, y, 0)); } } } } But unity responded by complaining that UnityException: Internal_CreateGameObject is not allowed to be called from a MonoBehaviour constructor (or instance field initializer), call it in Awake or Start instead. Called from MonoBehaviour 'Setup' on game object 'Map'. Is the engine even in a state where it can make objects when the InitializeOnLoad-ed static constructor happens? If this isn't possible, how are you supposed to visualize a procedurally generated map in Unity?
I'm not sure if Unity can do that, but you can work around it. Make a width and height variable, then expose them to the editor - either by making them public or by [SerializeField] attribute, and then edit them in the inspector while the editor is running the game. Use those variables for your x and y values in createMap(). Then you'd just have to add a section like this in Update(), to generate a new map on left click: if (Input.GetMouseButtonDown(0)) { // call generate map here } Check out this tutorial, too: https://unity3d.com/learn/tutorials/projects/procedural-cave-generation-tutorial/cellular-automata
You can create a custom editor for your Setup script. You can add a couple of buttons to it like Create and Destroy and, onClick, you can execute every code you want at edit-time as opposed to runtime. Here's the manual with useful informations on how to execute code in the editor and a video tutorial for writing Editor extensions.
Vector2 2D arrays in XNA
I am trying to create 2D vector2 arrays in XNA,C#. I used the following statement: Vector2[][] SpritePosition=new Vector2[4][]; Then I used the following for loop to initialize them: for(i=0;i<4;i++) { for(j=0;j<4;j++) { } } However, when I used the for loop, as stated above, it gave me an error, actually two: Int is a field and used as a type. 'for' is an invalid token in class, struct or interface member declaration. Can anyone tell why am I facing such a problem? EDIT: This is the code: public class Game1 : Microsoft.Xna.Framework.Game { int i=new int(); GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D texture; //Vector2[,] SpritePosition = new Vector2[4,4]; Vector2[,] SpriteSpeed = new Vector2[4,4]; for(i=0;i<4;i++) { } }
Seems you need to read up on some C# Tutorials You are going to need a function for that as so, void LoadArray() { for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { SpritePosition[i,j] = new Vector2(i,j) } } You can call it from your initialize method, using LoadArray() } Also, You dont need int i = new Int() for basic stuff like strings, ints, etc you dont need the new Whatever() part Just do for(int i=0;i<4;i++) { }
for #2, it means your for loop isn't inside of a function... so there's probably an extra } somewhere higher up in your code that you didn't mean to put there. there's a good chance that this is also the problem with #1 but you haven't really given us enough context (more code or where exactly the compiler says the error is) to say for sure