I am looking to do a specific task for a small unity game I am doing for class.
In it, I am trying to hash a class that contains variables and a method that is specific to each class created for the dictionary. The variables work fine as they do not need to be static and are not abstract, the method however I am struggling to work with.
Here is my entire script being used.
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
public class EnemyZ : MonoBehaviour
{
// Class used to hash names health assets and then move options
public class EnemySet
{
public string EnemyName { get; set; }
public float EnemyHealth { get; set; }
public void Moves()
{
}
}
//setting EnemySet with names health (assets and move options)
public static void Setup()
{
var cards = new Dictionary<string, EnemySet>()
{
{ "Slime", new EnemySet { EnemyName="Slime", EnemyHealth= 25} },
{ "Flaz", new EnemySet { EnemyName="Flaz", EnemyHealth= 34} },
{ "BandShee", new EnemySet { EnemyName="BandShee", EnemyHealth= 45} },
{"Fan-Natic", new EnemySet{EnemyName = "Fan-Natic", EnemyHealth = 20} }
};
}
void Update()
{
}
}
I am looking to have the Move function be overridden and callable.
How would I set this dictionary of class/method's up and how would I call it once it has been correctly added to the dictionary?
I think you are approaching your class structure and the approach to overwriting your abstract methods incorrectly which is causing the confusion around the dictionary.
I would create an interface for enemies that defines what your enemies need to contain and perform. You can then create an abstract base class the implements common functionality for enemies. Each enemy type should then inherit from your base class.
See below as an example:
// all enemy types must implement the following
public interface IEnemy {
string EnemyName { get; }
float EnemyHealth { get; }
void Move ();
}
// abstract base class for common functionality
public abstract class Enemy : IEnemy {
protected float speed = 0.1f;
public string EnemyName { get; protected set; }
public float EnemyHealth { get; protected set; }
public virtual void Move () {
Debug.Log ($"{EnemyName} moving at {speed}");
}
}
public class Slime : Enemy {
public Slime () {
speed = 0.1f;
EnemyName = "Slimer";
EnemyHealth = 100f;
}
public override void Move () {
Debug.Log ($"{EnemyName} moving at {speed}");
}
}
public class Flaz : Enemy {
public Flaz () {
speed = 0.5f;
EnemyName = "Flaz";
EnemyHealth = 50f;
}
public override void Move () {
Debug.Log ($"{EnemyName} moving at {speed}");
}
}
public class Test : MonoBehaviour {
readonly List<IEnemy> Enemies = new List<IEnemy> ();
void Start () {
var slimer = new Slime ();
Debug.Log ($"{slimer.EnemyName} initialized with health of {slimer.EnemyHealth}");
slimer.Move ();
var flaz = new Flaz ();
Debug.Log ($"{flaz.EnemyName} initialized with health of {flaz.EnemyHealth}");
flaz.Move ();
Enemies.Add (slimer);
Enemies.Add (flaz);
Debug.Log ($"Added {Enemies.Count} enemies");
}
}
I'd say that having a better understanding of classes and interfaces would be good if you wanted to store your enemies in code (the easy way if you're good at object oriented code).
However to answer the question directly I'm going to assume that you're loading enemies from a database or a file or some other non-code format.
In that case I'd go with something like
// Class used to hash names health assets and then move options
public class EnemySet
{
public string EnemyName { get; set; }
public float EnemyHealth { get; set; }
public Action Moves { get; set; }
}
Then when ever you are reading from your datasource to set up the dictionary of enemies I'd have something like
public class EnemyLoader
{
public void MovePawn() => console.WriteLine("I'm a chess piece");
public void MoveZombie() => console.WriteLine("Brains");
public void MoveSlime() => console.WriteLine("Wobbo");
public Action PickMovement(string moveType)
{
switch(moveType)
{
case "pawn": return MovePawn;
case "pawn": return MoveZombie;
default: return MoveSlime;
}
}
public Dictionary<string, EnemySet> LoadEnemies()
{
var dataSource = ReadDataFromSomewhere();
return dataSource.ToDictionary(
k => k.EnemyName,
v => new EnemySet
{
EnemyName = v.EnemyName,
EnemySpeed = v.EnemySpeed,
Move = PickMovement(v.MoveType)
});
}
}
Related
I want to create public Scene[] levels; and manually assign my levels to the array, then loop through it and generate level selection buttons, but it won't show up in the inspector.
Is there any workaround?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class True : MonoBehaviour
{
// Start is called before the first frame update
public static int money;
[SerializeField]
public SceneManager[] scenes;
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void nextscencefirst()
{
SceneManager.LoadScene("Level2");
money++;
}
}
SceneManager is a built-in static class to control scenes during runtime. We can go to any scene by calling LoadScene method from that class. Also SceneManager cannot be seen in Inspector since it cannot be serialized class.
A reminder: Your scenes should be listed on build settings for these to work.
There are three ways to do what you want to do:
Method 1. Create a string list that holds names of scenes
public List<string> sceneNameList = new List<string>;
Method 2. Create a list of indices of scenes that added in build settings.
public List<int> sceneBuildIndexList = new List<int>
If the names of scenes are long or somehow difficult or you want to use sceneBuildIndex value it would be good to create a class and reach scenes from it
[System.Serializable]
public class SceneData
{
public int sceneBuildIndex;
public string sceneKey;
//public void LoadScene() //Maybe
}
public List<SceneData> sceneDataList = new List<SceneData>();
public SceneData GetSceneData(string key)
{
for (int i = 0; i < sceneDataList.Count; i++)
{
if (sceneDataList[i].sceneKey == key)
{
return sceneDataList[i];
}
}
return null;
}
Method 3: In Unity most classes are created from UnityEngine.Object, so it may let you assign scene from the Inspector.
[System.Serializable]
public class SceneData
{
public UnityEngine.Object scene;
public string key;
public void LoadScene()
{
if (scene == null)
{
Debug.LogError("Scene is not assigned");
return;
}
string pathToScene = AssetDatabase.GetAssetPath(scene);
SceneManager.LoadScene(pathToScene); //If the scene is not set in BuildSettings it will throw an error.
}
}
public List<SceneData> sceneDataList = new List<SceneData>();
public SceneData GetSceneData(string key)
{
for (int i = 0; i < sceneDataList.Count; i++)
{
if (sceneDataList[i].key == key)
{
return sceneDataList[i];
}
}
return null;
}
private void Awake()
{
SceneData data = GetSceneData("Level1");
if (data != null)
{
data.LoadScene();
}
}
I am trying to make a vehicle changing game where there are 3 gameObject: car, tank and hover and if I press a button to change from car to a tank/hover i want them to be in a position where the car was.
the z axis is forward
I tried this code below but when I go backwards this will not work
UnityEngine;
public class Carswitcer : MonoBehaviour
{
public Transform car;
public Transform hover;
public Transform tank;
void Start()
{
car.gameObject.SetActive(true);
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(false);
}
public void Car()
{
if (car.position.z < hover.position.z)
{
car.position = hover.position;
}
if (car.position.z < tank.position.z)
{
car.position = tank.position;
}
car.gameObject.SetActive(true);
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(false);
}
public void Tank()
{
if (tank.position.z < hover.position.z)
{
tank.position = hover.position;
}
if (tank.position.z < car.position.z)
{
tank.position = car.position;
}
car.gameObject.SetActive(false);
tank.gameObject.SetActive(true);
hover.gameObject.SetActive(false);
}
public void Hover()
{
if (hover.position.z < car.position.z)
{
hover.position = car.position;
}
if (hover.position.z < tank.position.z)
{
hover.position = tank.position;
}
car.gameObject.SetActive(false);
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(true);
}
Why compare the z at all? Simply always copy the position of the currently active vehicle:
public class Carswitcer : MonoBehaviour
{
public Transform car;
public Transform hover;
public Transform tank;
// Store the current vehicle
private Transform currentVehicle;
void Start()
{
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(false);
SetActiveVehicle(car);
}
private void SetActiveVehicle(Transform newActiveVehicle)
{
// Is there a current vehicle?
if(currentVehicle)
{
// If so copy its position and set it inactive
newActiveVehicle.position = currentVehicle.position;
currentVehicle.gameObject.SetActive(false);
}
// Store the new active reference and set it active
currentVehicle = newActiveVehicle;
currentVehicle.gameObject.SetActive(true);
}
public void Car()
{
SetActiveVehicle(car);
}
public void Tank()
{
SetActiveVehicle(tank);
}
public void Hover()
{
SetActiveVehicle(hover);
}
I'm trying to set up an undo/redo system during runtime and couldn't get my code to work correctly. I have created an undo button, but when I moved my game object and press the undo button, the game object doesn't go back to its original states.
I just wanted to be able to have the end-user undo /redo his lastest action during runtime and not during Editor mode. I have made several attempts and have tried many different scripts but haven't made any progress. Any suggestions?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class undoit : MonoBehaviour
{
public class setting
{
public GameObject Obj;
public Vector3 Pos;
public Quaternion Rot;
public bool Deleted;
public void Restore()
{
Obj.transform.position = Pos;
Obj.transform.rotation = Rot;
Obj.SetActive(Deleted);
}
public setting(GameObject g)
{
Obj = g;
Pos = g.transform.position;
Rot = g.transform.rotation;
Deleted = g.activeSelf;
}
}
public List<setting> UndoList;
public void AddUndo(GameObject g)
{
setting s = new setting(g);
UndoList.Add(s);
}
public void Undo()
{
if (UndoList.Count > 0)
{
UndoList[UndoList.Count - 1].Restore();
UndoList.RemoveAt(UndoList.Count - 1);
}
}
void Start()
{
UndoList = new List<setting>();
}
}
First you need something you can store the relevant data to like e.g.
public struct ObjectState
{
// The transform this data belongs to
private Transform transform;
private Vector3 localPosition;
private Quaternion localRotation;
private Vector3 localScale;
private bool active;
public ObjectState (GameObject obj)
{
transform = obj.transform;
localPosition = transform.localPosition;
localRotation = transform.localRotation;
localScale = transform.localScale;
active = obj.activeSelf;
}
public void Apply()
{
transform.localPosition = localPosition;
transform.localRotation = localRotation;
transform.localScale = localScale;
transform.gameObject.SetActive(active);
}
}
Next I would use a wrapper class to store undo able changes like
public struct UndoableChange
{
private ObjectState _before;
private ObjectState _after;
public UndoableChange (ObjectState before, ObjectState after)
{
_before = before;
_after = after;
}
public void Undo()
{
_before.Apply();
}
public void Redo()
{
_after.Apply();
}
}
Or in case you need it for my objects at once you could use lists e.g. something like
public struct UndoableChange
{
private List<ObjectState> _before;
private List<ObjectState> _after;
public UndoableChange (List<ObjectState> before, List<ObjectState> after)
{
_before = before;
_after = after;
}
public void Undo()
{
foreach(var state in _before)
{
state.Apply();
}
}
public void Redo()
{
foreach(var state in _after)
{
state.Apply();
}
}
}
Then for the controller I would use two Stack. A Stack is "last in - first out" so exactly what you need. You add new entries using Push and by using Pop you retrieve the last added element and at the same time remove it from the stack. So it would look like
public static class UndoRedo
{
private static Stack<UndoableChange> undoStack = new Stack<UndoableChange>();
private static Stack<UndoableChange> redoStack = new Stack<UndoableChange>();
public static void Undo()
{
if(undoStack.Count == 0) return;
var lastAction = undoStack.Pop();
lastAction.Undo();
redoStack.Push(lastAction);
}
public static void Redo()
{
if(redoStack.Count == 0) return;
var lastAction = redoStack.Pop();
lastAction.Redo();
undoStack.Push(lastAction);
}
public static void AddAction(UndoableChange action)
{
redoStack.Clear();
undoStack.Push(action);
}
}
I dont know how exactly your user interacts with the objects but you now would always do something like
// at some point store the initial values
var before = new ObjectState (selectedGameObject);
//TODO Apply all your changes to the selected object
// get the new values
var after = new ObjectState (selectedGameObject);
// Log to the history
UndoRedo.AddAction(new UndoableChange(before, after));
or use lists for all selected objects accordingly e.g. simply using Linq
using System.Linq;
...
var before = selectedGameObjects.Select(obj => new ObjectState(obj)).ToList();
//TODO Apply all your changes to all the selected objects
var after = selectedGameObjects.Select(obj => new ObjectState(obj)).ToList();
UndoRedo.AddAction(new UndoableChange(before, after));
Note: Typed on smartphone but I hope the idea gets clear
Trying to make a Tile system in Unity. I have my tile class done but when I create a list in the TileManager class it gives me the option to change the size of the list but gives me an empty element with no changeable variables.
Tile Class:
public class Tile : MonoBehaviour
{
public Sprite tileSprite;
public string tileName;
public int tileID;
public Tile(Sprite newTileSprite, string newTileName, int newTileID)
{
tileSprite = newTileSprite;
tileName = newTileName;
tileID = newTileID;
}
void Start()
{
SpriteRenderer spriteRenderer = gameObject.GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;
if (spriteRenderer != null) spriteRenderer.sprite = tileSprite;
}
TileManager Class:
public class TileManager : MonoBehaviour {
public List<Tile> Tiles;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update () {
}
}
Use the System.Serializable attribute on Tile class. Also be sure that the Tile class does not inherit from MonoBehaviour, otherwise it will still not appear in the inspector list.
[System.Serializable]
public class Tile
{
public Sprite tileSprite;
public string tileName;
public int tileID;
public Tile(Sprite newTileSprite, string newTileName, int newTileID)
{
tileSprite = newTileSprite;
tileName = newTileName;
tileID = newTileID;
}
}
Here is the code I am using:
public enum TargetingOptions
{
NoTarget,
AllCreatures,
EnemyCreatures,
YourCreatures,
AllCharacters,
EnemyCharacters,
YourCharacters
}
public enum Faction
{
Fog,
Imperial,
Monster,
Pirate
}
public class CardAsset : ScriptableObject
{
public CharacterAsset characterAsset;
[TextArea(2, 3)]
public string Description;
public Sprite CardImage;
public int ManaCost;
[Header("Faction")]
public Faction faction;
[Header("Creature Info")]
public int MaxHealth;
public int Attack;
public int AttacksForOneTurn = 1;
public bool Charge;
public string CreatureScriptName;
public int specialCreatureAmount;
[Header("SpellInfo")]
public string SpellScriptName;
public int specialSpellAmount;
public TargetingOptions Targets;
}
what I am trying to do is create a different "hand" of cards depending on faction. to do this I have created a separate "hand" script for each of the factions, but I want to create a "handmultiple" script that will choose which "hand faction" script to head to.
So if in unity "pirate" is chosen, then all that will appear would be the associated scripts (ignoring fog, imperial and pirate scripts)
Does that make sense?
thank you!
enter code here using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
public class HandVisualImperial : MonoBehaviour
{
public AreaPosition owner;
public bool TakeCardsOpenly = true;
public SameDistanceChildren slots;
[Header("Transform References")]
public Transform DrawPreviewSpot;
public Transform DeckTransform;
public Transform OtherCardDrawSourceTransform;
public Transform PlayPreviewSpot;
private List<GameObject> CardsInHand = new List<GameObject>();
public void AddCard(GameObject card)
{
CardsInHand.Insert(0, card);
card.transform.SetParent(slots.transform);
PlaceCardsOnNewSlots();
UpdatePlacementOfSlots();
}
public void RemoveCard(GameObject card)
{
CardsInHand.Remove(card);
PlaceCardsOnNewSlots();
UpdatePlacementOfSlots();
}
So I think I have it figured out:
Have a whole bunch more work to do before testing it unfortunately.. but I believe I have it done now... Thank you all for the help!!!
public class HandVisualMultiple : MonoBehaviour
{
Faction factiontype;
public HandVisualFog fog;
public HandVisualImperial imperial;
public HandVisualMonster monster;
public HandVisualPirate pirate;
void Start()
{
fog = GetComponent<HandVisualFog>();
imperial = GetComponent<HandVisualImperial>();
monster = GetComponent<HandVisualMonster>();
pirate = GetComponent<HandVisualPirate>();
if (factiontype == Faction.Fog)
{
fog.enabled = true;
imperial.enabled = false;
monster.enabled = false;
pirate.enabled = false;
}
else if (factiontype == Faction.Imperial)
{
fog.enabled = false;
imperial.enabled = true;
monster.enabled = false;
pirate.enabled = false;
}
else if (factiontype == Faction.Monster)
{
fog.enabled = false;
imperial.enabled = false;
monster.enabled = true;
pirate.enabled = false;
}
else if (factiontype == Faction.Pirate)
{
fog.enabled = false;
imperial.enabled = false;
monster.enabled = false;
pirate.enabled = true;
}
}
}
It's not the answer for your question, but an experienced game developer advice.
Try to use Polimorfism. A single main class. "class Creature" and then a lot of child classes class ImperialCreature:Creature, class PirateCreature:Creature and then set the parameters to each of them.
And later you can set another sub classes. for instance: class JackSparrow:PirateCreature ... and on...
The enum is similar to an array, so if you do (int)Faction.Fog it will return 0 since it's the first item. You could have an array or list with your scripts in the same order as your enum and call them by casting the enum to an int