How can I manage different weapons in manager script? - c#

I'm making my rpg game in unity. As I need a lot of different weapons, I tried to make a script for each weapons. Then instead of enacting the Attack function in each weapon's object, I wanted to controll them in other class such as WeaponManager for some reason.
However, I got no idea how to manage variety of classes. It doesn't seem efficient to write all the codes for each classes, such as
if((gameObject).name=="Wand")
gameObject.Getcomponent<Wand>().Attack();
else if((gameObject).name=="Sword")
gameObject.Getcomponent<Sword>().Attack();
... and so on.
In other way, I also thought of SendMessage function, but it doesn't seem like efficient as well.
I'm wodering how can I solve this problem. Which method can I use?

Classical example use case for object oriented programming:
Inheritance!
Use a shared parent class both inherit from and either implement the method virtual with a shared default behavior the inheriting classes can overwrite/extend or make it abstract so inheriting classes have to implement it.
public abstract class Weapon : MonoBehaviour
{
public abstract void Attack();
// alternatively implement some default behavior
// in this case the child classes can but don't have to override this
//public virtual void Attack()
//{
// Debug.Log("Harr Harr .. but I'll do nothing else!", this);
//}
}
and then
public class Wand : Weapon
{
public override void Attack()
{
...
}
}
and
public class Sword : Weapon
{
public override void Attack()
{
...
}
}
then simply go
gameObject.GetComponent<Weapon>().Attack();

Related

Looking for a better way to extend methods

I'm working on a hobby project in Unity. I have MonoBehaviour scripts for my characters that use component objects for each behavior the character has. When I create new characters, I inherit a new class from the base class for whichever component behaves differently.
When certain triggers occur, I send characters back to their initial state by calling a Reset() method exposed by the base class that sets fields back to their initial values. I'm wondering how to send that call down through the inheritance chain. Right now, the base class has a protected virtual ChildReset() that gets called in Reset() and does nothing by default. If child classes have fields to reset, they override this method. It feels like a really awkward way of doing it.
I like the idea of implementing something similar to the messaging system Unity uses. If a monobehavior doesn't use the Update() message, then the class just doesn't define an update. It eliminates unnecessary calls. I have no idea how I would do something like that.
Any thought invested in this is much appreciated! I've written out the way my project is structured below just in case these details are useful for answers.
public class Character : MonoBehaviour
{
private Motion motionController;
private Interaction characterInteractionController;
//etc
private void Update()
{
motionController.DoStuff();
characterInteractionController.DoStuff();
}
private void Reset()
{
motionController.Reset();
characterInteractionController.Reset();
}
private void OnEnable() => ResetTrigger.OnReset += Reset;
private void OnDisable() => ResetTrigger.OnReset -= Reset;
}
public class Motion : Component {}
public class Interaction : Component {}
public abstract class Component
{
public void Reset()
{
/* set fields to default values */
ChildReset();
}
protected virtual void ChildReset() { }
public abstract void DoStuff();
}
There is no need to send a call down through the inheritance chain. You do not have two different objects. An object of the child class contains everything declared in the base class. Why not directly make Reset() virtual?
public abstract class Character : MonoBehaviour
{
public virtual void Reset()
{
...
}
}
public class ChildCharacter : Character
{
// If ChildCharacter has stuff to reset, override this method, otherwise don't!
public override void Reset()
{
base.Reset(); // Call this to reset stuff from the base class.
//TODO: reset child stuff.
}
}
If Reset is overridden in the child class, then calling Reset will call ChildCharacter.Reset() even if called on a variable statically typed as Character.
Character c = new ChildCharacter();
c.Reset(); // calls ChildCharacter.Reset() when overridden
If Reset is not overridden in the child class, then calling Reset will call Character.Reset() even if called on a ChildCharacter.
ChildCharacter child = new ChildCharacter();
child.Reset(); // calls Character.Reset() when not overridden.

How should you initialize constant data that needs parameters from another part of the code?

I don't think I explained my question very well in the title, so I'll do my best to do it here.
I have an abstract class called Song, a class that extends it MidiSongand then I have a SongCreator interface and a MidiSongCreatorclass that implements it. I would like to have a way to store lots of SongCreators so I can call their Create method but the problem is, since the SongCreators will each be a MidiSongCreator I am wondering how I should initialize each MidiSongCreator since it takes a MIDIPlayer and other things to help initialize it which doesn't have a static reference to it. Should I create a static class that holds lots of SongCreators? Should I not make the SongList class static?
What is looks like:
public abstract class Song{
public IList<Playable> notes { get; private set; }
public SongPlayData Start(){
// calls onStartEvent
return CreateSongData();
}
protected abstract SongPlayData CreateSongData();
public bool Update(SongPlayData songData, float songTime,List<SongPlayer> players) { // note that the players list is a list of people who are playing this game (this is a rhythm game) (doesn't have anything to do with MIDIPlayer
}
public void End(){
//calls end event
}
}
public class MidiSong : Song { // this is the class that needs the MIDIPlayer parameter
public MIDIPlayer midiPlayer;
protected MidiSong(MIDIPlayer player){
this.midiPlayer = player;
}
protected override SongPlayData CreateSongData() {
return new MidiSongData(midiPlayer);
}
}
public interface SongCreator<out T> where T : Song {
T Create();
}
public class MidiSongCreator : SongCreator<MidiSong>, IListenerObject { // this is the class that I need to store lots of instances of. the midiPlayer will probably be the same every time
private MIDIPlayer player;
public MidiSongCreator(MIDIPlayer player) {
this.player = player;
Init();
}
private void Init() {
player.midiListener.listener = this;
//
}
private void Clear() { // resets all the data so we can create another Song if we need to (even without entering stuff in)
if(player.midiListener.listener == this) {
player.midiListener.listener = null;
}
}
public MidiSong Create() {
MidiSong r = new MidiSong(player);
// I'm still going to implement calls to other methods from midiPlayer
Clear();
return r;
}
public void OnLoad(MidiFile file) {
// does stuff to load midi file (deals with individual events)
}
}
public class MasterSong : MonoBehaviour { // this should initialize last btw (It's in the Script Execution Order)
public MIDIPlayer midiPlayer;
public Song song;
public SongPlayData playData;
// Use this for initialization
void Start() {
// this is where I'd like to reference a SongCreator and call it's create method and Start the song
//for instance:
song = SongList.SONG_NAME.Create();
playData = song.Start();
}
void Update() {
}
}
It's a RhythmGame made with unity, but I didn't add the unity tag because I feel that this is more of a C#/design thing.
Also note, that I have my classes much more organized that just one file with all these.
I'm looking for ways to improve on the design that I have.
This is a design problem, domain design!
I suggest don't write code yet. Create a class diagram, with pen and paper, don't need to use tools in the beginning.
Try to determine entities - classes, interfaces etc - and the relationship among them. Just use boxes and arrow, don't need to put details yet. With boxes and arrows, you will be able to have a picture of your domain much more clearly. Keep refining and changing it, still at this high level, without details, until you are satisfied.
Then, step by step, refine it by adding details/properties such attributes and methods. This may cause to change the diagram from the first step.
I intentionally did not refer to specifics of you questions like the classes and interfaces you mentioned. Since, there is not enough information to comment on that. Secondly, and more importantly, you should start at high level of design and once that is done, then do coding based on your design.

How do I know what to put in my interfaces?

I've come across this recurring problem when trying to design the logic for my programs. Let's say I have a IDriveable interface.
interface IDriveable
{
public void Drive();
}
Then a car class that implements this (c#) syntax:
class Car : IDriveable
{
public void Drive(){
//Do the movement here.
}
}
Here's where my problem occurs. If I am designing a game, the car doesn't drive itself, a player should drive the car, surely that makes sense?
class player
{
public void Drive(IDriveable vehicle){
vehicle.Drive();
}
}
It feels like I am 'ping-ponging' the logic around which doesn't seem right.
A better way to structure your code might be something like this:
class Player // Start class names with a capital letter
{
Car thisPlayersCar; // Initialize it the constructor or somewhere appropriate
public void someFunction() {
thisPlayersCar.Drive();
}
}
Basically, the purpose of an interface is that wherever you call thisPlayersCar.Drive(); (or Drive() on any IDriveable), you're guaranteed that the object will have a Drive() function ready to go.

override an abstract function with a virtual one?

Basically i'm asking how best to do this properly based on what I THINK I want.
I have a weapon class which is abstract because it contains methods like Fire() that are so general (lasers vs bullets) in how they'd fire they require the inheritor implement.
however once I get to an actual ballistic weapon class which inherits from weapon class.
well most of those fire the same. They spawn the projectile and lower the ammo count and wait for the reload timer to kick down to 0 before allowing fire again.
Whether it's a tank gun or a pistol or a turret or an artillery it will stay the same more than change.
That seems like a virtual class (a torpedo launcher might need a special implementation to raise and lower a hatch door to fire so it can't be non-virtual/abstract, it needs to be modifiable)
however basically I want to have
abstract weapon class Fire() method
virtual ballistic weapon inherits weapon Fire() method
But it doesnt seem that you can override an abstract method and make it a virtual one.
I don't think I want a whole new method though because i'd like to be able to do
weapon.fire
and it go into ballstic weapon and use the implementation there, unless the specific ballistic weapon has overridden that general method.
Abstract means it must be overridden in child class where virtual means it can be overridden. You can have virtual methods in an abstract class. You can solve your problem using Interfaces by binding in a form of contract like:
Ifireable
May be you can do like that:
abstract class Weapon
{
public abstract void Fire();
}
class BallisticWeapon
{
virtual public void Fire()
{
// Ballistic fire
}
}
// Weapon need ballistic fire
class WeaponA : BallisticWeapon
{
}
// Weapon don't need ballistic fire
class WeaponB : Weapon
{
// Implement fire
public void Fire()
{
}
}

C# Base or Template Class?

Ok so I am making a game using XNA, I would like all of the enemies to extend from one base class called "baseEnemy.cs". For example, I would like a zombie to have a class called "zombie.cs" but make it entend the "baseEnemy.cs".
I think I remember being told its would be laid out like:
class zombie : baseEnemy
{
}
But I am assuming the use of get{} and set{} would help me to change values of current variables in zombies.cs that exist as part of baseEnemy.cs... If that makes sense? I don't understand the usage of get{} and set{} but I have seen it in other languages (such as the code for minecraft) which I would assume are similar in their working.
So, say I have a float for the speed of the enemy... I don't want all the enemies to move at the same speed (zombie's should have a low speed, etc). How could I get the speed from the baseEnemy.cs and set it as the speed in zombie.cs.
Or would I be better just making the variables public in baseEnemy.cs?
Sorry if the above doesn't make much sense, I am not too experienced with XNA or terminology used and therefore I probably sound like I am going round in circles :S
You are looking for so called abstract methods or abstract properties.
abstract class Enemy
{
public abstract float GetSpeed();
}
class Zombie : Enemy
{
public override float GetSpeed()
{
return 10;
}
}
Note the abstract keyword preceding the class name and the method name. The child class has to implement all abstract members, if it is not abstract itself. When an abstract member is implemented the override keyword must be used.
The get set syntax you are describing is called a property. It is a special C# construct that organizes the getter and/or setter of a field and puts them in a single block. The same example as above using properties:
abstract class Enemy
{
public abstract float Speed { get; }
}
class Zombie : Enemy
{
public override float Speed
{
get { return 10; }
}
}

Categories