Fetching variables from an NPC handling/tracking class (C#/XNA) - c#

I apologize as I don't really know how to describe my problem.
I'm working on a game which contains hundreds of levels, and will consequently contain hundreds of different NPCs. Since this is an MMO-style game where the states of all NPCs will need to be preserved, I've settled on creating a class that keeps track of each instance of every NPC for me, and when a player enters a level, it can provide the list for all of the NPCs in that area, where they go, etc. The problem is that since this NPC Control class stores a multitude of different classes, C# won't allow me to grab the information I need from them such as their x/y/image in my Draw method of my Game1 class since it doesn't know that those variables exist.
My current NPC control class looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyWorld
{
public class NpcsControl
{
public List<object> GetNPCs(string levelname)
{
List<object> NPCs = new List<object>();
switch (levelname)
{
case "garden2":
NPC_TreeBreakable NPCtree = new NPC_TreeBreakable();
NPCtree.x = 16;
NPCtree.y = 16;
NPCs.Add(NPCtree);
NPCtree = new NPC_TreeBreakable();
NPCtree.x = 16;
NPCtree.y = 512;
NPCs.Add(NPCtree);
NPC_TestLamp NPCtestlamp = new NPC_TestLamp();
NPCtestlamp.x = 16;
NPCtestlamp.y = 1024;
NPCs.Add(NPCtestlamp);
break;
}
return NPCs;
}
}
}
While my tree NPC code looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyWorld
{
public class NPC_TreeBreakable
{
public string image = "tree_new";
public int x;
public int y;
}
}
I need to fetch this in my Game1.cs so that I can show the game where to draw these. Currently it is using this code:
foreach (object o in npcControl.GetNPCs(players[0].level))
{
object ob = o.GetType(); //ob == MyWorld.NPC_TreeBreakable
imgpos.X = ((NPC_TreeBreakable)(o)).x;
imgpos.Y = ((NPC_TreeBreakable)(o)).y;
SpriteTexture = Content.Load<Texture2D>(((NPC_TreeBreakable)(o)).image);
spriteBatch.Draw(SpriteTexture, imgpos, new Rectangle(0, 0, SpriteTexture.Width, SpriteTexture.Height), Color.White);
}
The problem is that I'm being forced to manually input "((NPC_TreeBreakable)(o)).x" or "((NPC_TestLamp)(o)).x" instead of being able to just use ((ob)(o)).x since C# knows that not every class may have an "x", "y", or "image" variable. Even though they will be in each of the NPCs.
How do I fix this so that I can reference variables when C# doesn't know to expect them like this?
Thank you!

Related

Ho to paste stored components properties values to another project and also how to format the properties in string[]?

In one project I have editor window script with the copy :
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System.Reflection;
using UnityEditorInternal;
using System.Text;
using System.Linq;
public class EditorWindowCopyComponentsData : EditorWindow
{
[MenuItem("GameObject/Copy Components", false, 20)]
public static void CopyComponents(MenuCommand command)
{
var go = Selection.activeGameObject;//command.context as GameObject;
var components = go.GetComponents<Component>().ToList();
foreach (Transform child in go.transform)
{
foreach (Component component in child.GetComponents<Component>())
{
components.Add(component);
}
}
var serializedData = new string[components.Count];
for (int i = 0; i < components.Count; i++)
{
// Type-AssemblyQualifiedName : Json-Serialized-Data
serializedData[i] = components[i].GetType().AssemblyQualifiedName + ":" + EditorJsonUtility.ToJson(components[i]);
}
EditorGUIUtility.systemCopyBuffer = string.Join("\n", serializedData);
}
}
When I'm using a break point and put the mouse over the serializedData I see the 6 components properties and values of the properties but they are format different for example screenshot of serializedData[0] content :
And if I'm copying all the serializedData that is in the CopyBuffer to empty notepad text file :
And I want somehow to format the serializedData to looks like the components for example components[0] content :
So when paste whats in CopyBuffer for example to Notepad the format should be for example :
Component[0]
Properties :
childCount = 1
eulerAngles = "(12.8, 179.1, 0.0)"
And so on...
Then
Component[1]
Properties :
etc....= 45546
etc property = fsdfsdf
This format like it's in the components
And to add for an option also to add sub properties to the Copybuffer or to the format for example on the property eulerAngles on it's left side there is a small arrow and if you put the mouse on it it will show some deep level more properties and values so as an option to format and how them too in the CopyBuffer when copy the components.
Second problem is when make a paste in the other project :
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System.Reflection;
using UnityEditorInternal;
using System;
public class EditorWindowCopyComponentsData : EditorWindow
{
[MenuItem("GameObject/Paste Components", false, 21)]
public static void PasteComponents(MenuCommand command)
{
var go = Selection.activeGameObject;//command.context as GameObject;
var serializedData = EditorGUIUtility.systemCopyBuffer.Split('\n');
char[] splitter = { ':' };
foreach (var data in serializedData)
{
var typeAndJson = data.Split(splitter, 2);
var type = Type.GetType(typeAndJson[0]);
if (type.FullName == "UnityEngine.Transform") // only 1 transform
{
EditorJsonUtility.FromJsonOverwrite(typeAndJson[1], go.transform);
}
else
{
EditorJsonUtility.FromJsonOverwrite(typeAndJson[1], go.AddComponent(type));
}
}
}
}
It will not put the childs components as childs of 'go' like when copy.
In the copy I loop also over the child components and add them to the components List and copy them too :
foreach (Transform child in go.transform)
{
foreach (Component component in child.GetComponents<Component>())
{
components.Add(component);
}
}
But how can I add them in the same order and place as childs also when paste it ?

Variables in other scripts in Unity

I am trying to create a game in Unity 2d. I have finished most of what I want to do and have moved on to the enemies. The enemies (dragons) come in from different points of screen. To do this I have placed sprite game objects where I want the dragon to spawn. I have made all of these objects a child of another object called DragonAncores. I attached a script to DragonAncores which says this...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragonTracker : MonoBehaviour {
// is gold dragon in play?
public bool GoldDragonInit = false;
// curently active dragons
public int DragonCount = 0;
// defalts to 5
public int Difficulty = 5;
}
I am then attaching a script to each sprite which will eventually summon in a dragon Prefab (containing 2 colliders and an animator) biased of If statment logic derived from the other variables.
Below is the code I am using.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2)
DragonType=RNG.Next(0,101)
if (RSpawn = 1) ;
{
if (LiveDragons > DifLev) ;
{
if (DragonType > 99) ;
{
// summon regular dragon
}
if (DragonType = 100) ;
{
if (GoldDragonSpawn = true) ;
{
// summon gold dragon
}
}
}
}
}
}
This is throwing up this error list.
This shows my hierarchy in unity and the anchor points (the Squair crosshair looking things)
I have looked for other threads that adress this topic and they all try different methods, none work.
I am using Unity 2018.2.18f1
There are a few errors in your code here. The following is incorrect.
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
The correct way to access this, seeing as you said DragonAncors is the parent would be:
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
This sets the GoldDragonInit Boolean to the value of GoldDragonSpawn. This has to be inside a function, as you have it outside of a function I presume you needed this set on start. Therefore I have placed it in the void Start() function. This is called at the start of the game(loaded scene).
You also do not need semi-colons ; after an if statement, however it does need to appear after every line of difinitive code. The code you have provided should instead look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
void Start()
{
// variables from other script
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2);
DragonType=RNG.Next(0,101);
if (RSpawn = 1)
{
if (LiveDragons > DifLev)
{
if (DragonType > 99)
{
// summon regular dragon
}
if (DragonType = 100)
{
if (GoldDragonSpawn = true)
{
// summon gold dragon
}
}
}
}
}
}
This works because DragonTracker is a script in the objects parent. If this was not the case then GetComponentInParent().GoldDragonInit = GoldDragonSpawn; would be replaced like so:
[SerializeField]
private GameObject DragonAncors;
void Start()
{
DragonAncors.GetComponent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
This is not valid c# code:
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
Why? Because it isn't inside a method.
Also, the comment is wrong. It isn't getting a variable (typo, too), its setting a variable in another script!
The reason for the first...16 problems Visual Studio is complaining about are because of this line.
At this location you are only allowed to declare methods, fields, and properties and you're currently trying to access another class and change one of its members (something you can only do inside a method).
Additionally, you have .cs which I assume is because "DragonAnchors.cs is the file name!" which you don't need to do. I'm not sure how to go about rewriting this line (inside Start()) as I'm not sure what you're trying to actually do. That is, I don't know where an instance of DragonAnchors actually resides. You're calling GetComponent(), which is typically reserved for accessing components attached to game objects, but as you've attempted to do so on a static class reference, I'm not sure if you meant to call it on this or on something else.
This is how you can get to DragonTracker:
DragonTracker dt = GameObject.Find("DragonAncores").GetComponent<DragonTracker>()
Debug.Log(dt.DragonCount);
There are a lot of errors there and they may take some steps to go through, but first things first you should clear up the issue with the code you're trying to use being unsupported. Go into the project settings and change the compiler language version as it notes on the 5th error down. This should allow you to use the newer functionality.

Create ARFaceGeometry from BlendShape dictionary

I am trying to create an ARKit Mesh based on the blendShape dictionary I create (in a Unity script). Basically, I want to get a mesh and modify one value in the dictionary and create the geometry back using this new dictionary.
I found the method init in the ARFaceGeometry documentation but I do not understand how exactly to call it.
With the help of Face Tracking examples on the Unity blog I have come up with code that looks like:
using UnityEngine;
using System.Collections;
using UnityEngine.XR.iOS;
using System.Collections.Generic;
public class RetrieveBlendShapesARKit : MonoBehaviour
{
private UnityARSessionNativeInterface m_session;
Dictionary<string, float> currentBlendShapes;
// Use this for initialization
void Start()
{
currentBlendShapes["eyeBlink_L"] = 1.0f;
Debug.Log("---------------Get ARKit Mesh Start-------------------");
m_session = UnityARSessionNativeInterface.GetARSessionNativeInterface();
ARKitFaceTrackingConfiguration config = new ARKitFaceTrackingConfiguration();
config.alignment = UnityARAlignment.UnityARAlignmentGravity;
config.enableLightEstimation = true;
if (config.IsSupported)
{
m_session.RunWithConfig(config);
UnityARSessionNativeInterface.ARFaceAnchorAddedEvent += FaceAdded;
UnityARSessionNativeInterface.ARFaceAnchorUpdatedEvent += FaceUpdated;
}
}
void FaceAdded(ARFaceAnchor anchorData)
{
anchorData.faceGeometry.init(currentBlendShapes);
}
void FaceUpdated(ARFaceAnchor anchorData)
{
anchorData.blendShapes = currentBlendShapes;
}
// Update is called once per frame
void Update()
{
}
}
I get an error saying ARFaceGeometry does not contain a definition of init and I also get an error that I cannot assign blendShapes since it's read-only (I just thought of trying direct assignment which didn't work).
I would appreciate any help in using this init method, or any other way to create the ARKit mesh on iPhone using a custom blendShape dictionary through Unity.
Thanks!

LSPDFR Spawn Point Error

I am an LSPDFR developer and I am building a callout. I am trying to get my spawn point to spawn at a certain coordinate.
I currently have:
SpawnPoint = World.GetNextPositionOnStreet(new Vector3(-2051.99463, 3237.05835, 1456.97021));
This is in C#. I get the error Argument 1: cannot convert from 'double' to 'float'. Does anyone know what to do? And I am also writing this in Visual Studio
I have tried removing:
private Vector3 SpawnPoint;
You need to add 'J' to the end of each coordinate.
This is a very old topic, but there are no answer ..
I searched for LSPDFR -An addon for GTAV. I had hoped for a lot of activity
There was not..
Anywitch
Someone may browse for this so i will add the way to do this in LSPDFR
Firstly To get a point inside the game-world:
Press F4
In console write:
PrintInfo
Press Enter
Now you have 3 values that represent a 3Dvector
Z is UP!
Lets say we got
x:111, y:222 z:10
Script:
In your variables define a spawnpoint
public Vector3 SpawnPoint;
You can now say
SpawnPoint = new Vector3(111.0f, 222.0f, 10.0f);
and use that as a position for a object.
You can also use an object, Ped, or your player, and set a spawnpoint close by:
SpawnPoint = World.GetNextPositionOnStreet(
Game.LocalPlayer.Character.Position.Around(300f));
That will make a spawn around 300 world meters from current pos your player is in.
So that is two different ways to make a spawn-position in LSPDFR.
CODE
//will spawn a car 10 tiks from player if key J is pressed
using LSPD_First_Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rage;
using Rage.Native;
using LSPD_First_Response.Mod.API;
using LSPD_First_Response.Mod.Callouts;
using LSPD_First_Response.Engine.Scripting.Entities;
using Rage.Attributes;
[assembly: Plugin("spawn car", Description = "spawns a car", Author = "mb")]
namespace Example
{
public static class SpawnCar
{
private static void Main()
{
public Vector3 SpawnPoint;
while (true)
{
if (Game.IsKeyDown(System.Windows.Forms.Keys.J))
{
SpawnPoint = World.GetNextPositionOnStreet(
Game.LocalPlayer.Character.Position.Around(10.0f));
aaVehicle = new Vehicle("BALLER", SpawnPoint);//100 of cars are available
}
GameFiber.Yield();
}
}
}
}

How can I get the text and save it in InputField?

I have an array InputFields, in my situation - six InputFields. How can I get the text and save it from each InputField?
Everything should work like this: I turn on the app, I change one, two or all of the input field, then turn off the application. Again, I turn on, and input fields have to be values which I wrote earlier.
I have a code that must seem to work properly, but this code works like this: it takes only the last value entered by the user and writes it to the last input field.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SaveText : MonoBehaviour {
public GameObject[] InputFields;
public static string n;
public void Start ()
{
for(int i = 0; i < InputFields.Length; i++)
{
InputFields[i].GetComponent<InputField> ().text = PlayerPrefs.GetString (InputFields[i].name);
Debug.Log (PlayerPrefs.GetString (InputFields[i].name));
n = InputFields[i].name;
var input = InputFields[i].GetComponent<InputField> ();
var se = new InputField.SubmitEvent ();
se.AddListener (SubmitName);
input.onEndEdit = se;
}
}
public void SubmitName(string arg)
{
PlayerPrefs.SetString (n, arg);
}
An array of input fields I initialize dragging in Unity each input field in free cell in Script Component.
Well, you are using a single variable for all the different player prefs variables, n. So when you change a field (with SubmitName) it uses that n pref variable and changes it. This will correspond to the last value you gave to n in your loop.
An option would be to have that be an array too (private string prefValues[]) or to pass the calling input field to SubmitName (or rather it's name) and use that instead of n.
A little example (from your code you can actually change your GameObject[] to InputField[] which saves some GetComponent calls):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EventTest : MonoBehaviour
{
public InputField[] inputFields;
void Start ()
{
foreach(InputField field in inputFields)
{
var se = new InputField.SubmitEvent();
se.AddListener(delegate {
SubmitText(field.name, field.text);
});
field.onEndEdit = se;
}
}
public void SubmitText(string prefKey, string prefVal)
{
Debug.Log("Saved " + prefVal + " to " + prefKey);
}
}
Edit:
Added lines for saving/loading from prefs in the code below. For my test setup I just have two scenes. Both have two input fields and a button that calls that scene change. I can type stuff on the fields as I like, change scenes and it stays. Also upon restarting playmode in the editor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class EventTest : MonoBehaviour
{
public InputField[] inputFields;
void Start ()
{
foreach(InputField field in inputFields)
{
// Get field values from player prefs if existing
// (it should only not exist the very first time or if you delete/clear the prefs)
if(PlayerPrefs.HasKey(field.name))
field.text = PlayerPrefs.GetString(field.name);
var se = new InputField.SubmitEvent();
se.AddListener(delegate {
SubmitText(field.name, field.text);
});
field.onEndEdit = se;
}
}
public void SubmitText(string prefKey, string prefValue)
{
// store the typed text of the respective input field
PlayerPrefs.SetString(prefKey, prefValue);
}
public void ChangeScene()
{
if(SceneManager.GetActiveScene().name == "Scene1")
{
SceneManager.LoadScene("Scene2");
}
else
{
SceneManager.LoadScene("Scene1");
}
}
}
If you want to save the data, after the application stopped or if it gets paused, you need to add some more functions to your MonoBehavior so that it can handle additional actions on a system end or pause.
Look at following functions in the documentation
OnApplicationQuit()
OnApplicationPause()
These functions will be called automatically. Inside these functions you need to iterate through the textfields and save them. It is one of the last functions that will be called before the program will lose it's recourses.
Regarding the Scene change, you would need the new scene to tell the old one to do any further actions. Like you can read in this post, there is only a way to know if a new scene was loaded.
Maybe you have a place where you can save the last active scene object and then call the last scenes function to do the saving process while the object is not cleared.

Categories

Resources