Looking for advice on getting events to work - c#

I'm looking for some help in understanding Events. I've been reading articles on them and watching tutorial videos.
I almost understand them, but I keep hitting snags.
I made myself a simple WinForms test app to try and learn the process. In the app, there are 2 walking sprites running around the screen.
When you click on the form, it creates a sprite of a falling thwomp (And it creates an event) the walker sprites should react to the event by choosing a new walking path headed away from the sprite. I think I've written everything correctly, but when I compile it I get the error:
Error 1 Inconsistent accessibility: parameter type 'eventStomper.RunEventArgs' is less accessible than delegate 'eventStomper.RunInFear'
Error 2 Inconsistent accessibility: parameter type 'eventStomper.RunEventArgs' is less accessible than method 'eventStomper.Walker.RunAway(object, eventStomper.RunEventArgs)'
I'm at a loss because everything is public. Any suggestions on there error? And, any advice on Event handling?
Here's the source code boiled down to just the relevant bits:
namespace eventStomper
{
public delegate void RunInFear(object sender, RunEventArgs re); //The delegate for responding to events.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
spawnWalkers(); //Create a couple of walkers to roam around the form
}
List<Thwomp> thowmpList = new List<Thwomp>(); //List of thwomps. This is iterated through for animation.
List<Walker> walkerList = new List<Walker>();// Same thing with the walkers.
public void pictureBox1_Click(object sender, EventArgs e) //When you click on the background, it spawns a thwomp
{
Point _spawnPoint = this.PointToClient(Cursor.Position);
Thwomp _thwomp = new Thwomp(_spawnPoint, sprite ); //Generate a new Thwomp
thowmpList.Add(_thwomp); //Add it to the list of Thwomps
_thwomp.TimeToRun += walkerList[0].RunAway; //Register with the two walkers roaming around.
_thwomp.TimeToRun += walkerList[1].RunAway;
//Do other things to setup the thwomp sprite
}
}
public class Thwomp
{
public int spriteX = 0;//Current sprite location
public int spriteY = 0;
public int targetX = 0;//Where the thwomp will land.
public int targetY = 0;
public event RunInFear TimeToRun;
public void Animate()
{
//Do Animation steps.
}
public Thwomp(Point spawnPoint, PictureBox spriteIncoming)
{
RunEventArgs re = new RunEventArgs();
re._pointOfFear = spawnPoint;
//Setup thwomp sprite
TimeToRun(this, re); //Trigger the event.
}
}
public class Walker
{
public int spriteX = 0; //Current sprite location
public int spriteY = 0;
public Walker(Point spawnPoint, PictureBox spriteIncoming)
{
//Create the walker
}
public void RunAway(Point dangerPoint)
{
if (Math.Abs(sprite.Top - dangerPoint.Y) < 20 && Math.Abs(sprite.Left - dangerPoint.X) < 20) //If near a newly created thwomp, run away.
{
//Pick a path headed away from the danger.
}
}
public void Animate()
{
//Move the walker away.
}
}
class RunEventArgs : EventArgs
{
public Point _pointOfFear;
}
}

I'm at a loss because everything is public.
Not quite. As the error message says:
parameter type 'eventStomper.RunEventArgs' is less accessible than delegate 'eventStomper.RunInFear'
According to that message, RunEventArgs is less accessible than RunInFear. Therefore, let's look at the accessibility levels of those two types:
public delegate void RunInFear(object sender, RunEventArgs re);
So, that is public. So far, so good.
class RunEventArgs : EventArgs
{
public Point _pointOfFear;
}
Aha! This one has no accessibility assigned to, which means that - according to the docs - it will default to internal:
Top-level types, which are not nested in other types, can only have internal or public accessibility. The default accessibility for these types is internal.
Therefore, make that RunEventArgs class public and your code should compile.

Related

Access to the variable between multiple scripts for loose coupling in Unity3D

For instance, assume we have a health variable in one script and want to access to this variable in UI script for showing health and on the other hand change pitch of sound according to amount of health.
I know some ways to access this variable but they highly dependent scripts to each other. Like GetComponent, Singelton or even C# event.
I want to know is there any patterns to decrease these dependencies ? (Also efficient and performant)
Edit:
This event system is in my mind but I think It has some dependencies which they can be removed but I don't know how and also I'm not sure about it and its performance.
public class Player {
public class HealthEventArgs : EventArgs {
public int currentHealth;
public HealthChangedEventArgs(int currentHealth) {
this.currentHealth = currentHealth;
}
}
public static event EventHandler<HealthEventArgs> HealthEvent;
public void NotifyHealthChanged(int health) {
if(HealthEvent != null) {
HealthEvent.Invoke(this, new HealthEventArgs(health));
}
}
}
public class OtherClass {
public void SubscribeToPlayerHealthEvent() {
Player.HealthEvent += Foo;
}
public void UnsubscribeFromPlayerHealthEvent() {
Player.HealthEvent -= Foo;
}
public void Foo(object o, HealthEventArgs e) {
//Do something with e.currentHealth
}
}
after # julxzs and i were talking...
did a little checking on best practices... i found this
link
so it turns out singleton is the way to go, check out the page, lots of good info! thx #julxzs
Take a look at dependency injection technique.
I can advice to start with Zenject.

How do I get a code block to run when a variable changes?

I'm writing a code for a small game so naturally, I have a lot of variables that all do different things whenever they change. My question is, is it possible to tell my code to run certain lines of code each time the variables change, so as to avoid repeatedly typing in the same lines, over and over again?
Example:
Hero1.Health = 10;
Hero1.MaxHealth = 12;
ProgressBarHeroHealth1.Max = Hero1.MaxHealth;
ProgressBarHeroHealth1.Value = Hero1.Health;
If I change the hero's health, I want the Progress Bars value to automatically change with it, without having to write it down every time that it changes.
I don't know if it's at all possible, or if I'm just hoping for too much, so I'm coming to you for answers. Any info would be much appreciated.
Beginner
You could write an extension method for the control, e.g.
static class ExtensionMethods
{
static public void SetHealth(this ProgressBar control, Hero hero)
{
control.Max = hero.MaxHealth;
control.Value = hero.Health;
}
}
And call it like this:
ProgressBarHeroHealth1.SetHealth(Hero1);
Intermediate
Now extension methods are cool and all, but this is like the dumbest way to use them. If the control class isn't sealed, a better design is to write a proper method:
public class HealthBar : ProgressBar
{
public void SetHealth(Hero hero)
{
this.Max = hero.MaxHealth;
this.Value = hero.Health;
}
}
And call it the same way:
HealthBarHeroHealth1.SetHealth(Hero1);
(In order for this to work, you have to use HealthBar instead of ProgressBar in whatever UI platform you're using).
Advanced
But you know what would be really cool? A progress bar that updates itself. Hmmm... maybe if it listened for some kind of event....
class HeroHealthBar : ProgressBar
{
protected readonly Hero _hero;
public HeroHealthBar(Hero hero) : base()
{
_hero = hero;
hero.HealthChanged += this.Hero_HealthChanged;
}
public void Hero_HealthChanged(object sender, EventArgs e)
{
this.Max = _hero.MaxHealth;
this.Value = _hero.Health;
}
}
Of course you'd need a Hero class that raised the event....
class Hero
{
public event EventHandler HealthChanged;
public void SetHealth(int current, int max)
{
_health = current;
_maxHealth = max;
OnHealthChanged();
}
protected void OnHealthChanged()
{
if (HealthChanged != null) HealthChanged(this, EventArgs.Empty);
}
}
Once all this setup is done, when you want to set the health, all you have to do is write
_hero.SetHealth(current,max);
...which will set it on the hero and automatically notify the progress bar to update itself as well.
It is possible. Suppose You have a class called Hero and you want to change progress bar colour when healt is increase or decrease then
Class Hero
{
Public String Name{get; set;}
Public Double health;
Public Double Health
{
get
{
return health;
}
set
{
//Here you can place a function which can easily check the value of health and work accordingly.
ProgressBarColorChange(value+health);
health = value;
}
}
Public void ProgressBarColorChange(double health)
{
//Color Change Fuction Implementation
}
}

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.

XNA - How Can I Edit A Rectangles Bounds From Another Class

So I have this problem where I have a variable called StarBounds in my main game class file. The file is now called MainGameClass.cs with namespace StarCatcher.
I have made a class to detect if the mouse is hovered over the StarBounds variable and then clicks. On click I would like to edit the StarBounds variable from this other class called GameFunctions.cs.
I am able to do something like...
MainGameClass mgc = new MainGameClass();
The when the hover and click event is triggered I can type in without errors:
mgc.StarBounds = new rectangle(0,0,0,0);
But in the actual game it does not change. And also I sometimes get errors when doing the "mgc.StarBounds = new rectangle(0,0,0,0);" saying it doesn't have an object reference.
I think this is most likely just a scope issue. The exception is caused because mgc is null. Make sure that GameFunctions has not declared a local copy of MainGameClass and is referencing a pre-existing instance. Otherwise, use a static variable for StarBounds as shown in the example. Example,
public class MainGameClass {
public static Rectangle StarBounds;
public void HandleInput () {
// if GameFunctions.ClickedWithinStarBounds(mouse)
// GameFunctions.OnClickStarBounds()
}
}
public class GameFunctions {
public static void ClickedWithinStarBounds(MouseState mouse) {
// create a rectangle around the mouse (ie. cursor) for the detection area
// return left mouse button is down or pressed && IsWithinStarBounds
}
public static bool IsWithingStarBounds(Rectangle area) {
return (MainGameClass.StarBounds.Intersects(area);
}
public static void OnClickStarBounds() {
MainGameClass.StarBounds = Rectangle.Empty;
}
}

Problem creating instance of a class

I've create the following class in Visual Studio 2010:
public class Bat : Form1
{
public int BatLocation;
public void draw()
{
Pen batPen = new Pen(Color.Black);
batPen.Width = 10;
playArea.DrawRectangle(batPen, BatLocation, (picPlayArea.Height - 30), 50, 10);
}
}
But when I try to create an instance of the class, I get a stack overflow exception, advising me to make sure that I don't have an infinite loop or infinite recursion. I have tried creating the instance two different ways, as below:
Bat bottomBat;
bottomBat = new Bat();
and
Bat bottomBat = new Bat();
But both ways return the same error when I try to run the program. I've also tried the class definition with and without the public modifier.
I'm pretty new to programming and have no idea what might be causing this problem. Am I doing something wrong?
Edit: The code for the Bat class is everything I have at the moment, haven't created a specific constructor for it... Didn't think I needed to?
Anyway, here is the Form1 class in its entirety:
public partial class Form1 : Form
{
// Define various objects for the game
public Graphics playArea;
Bat bottomBat = new Bat();
public Form1()
{
InitializeComponent();
// Create instances of objects
playArea = picPlayArea.CreateGraphics();
//bottomBat = new Bat();
// Delegate the mouseMove event for picPlayArea
picPlayArea.MouseMove += new MouseEventHandler(picPlayArea_MouseMove);
}
private void picPlayArea_MouseMove(object sender, MouseEventArgs e)
{
bottomBat.Location = e.X;
}
private void btnExit_Click(object sender, EventArgs e)
{
string msg = "Are you sure you want to exit?",
title = "Confirm Exit";
DialogResult res = MessageBox.Show(msg, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (res == DialogResult.Yes)
{
Environment.Exit(0);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
// This is where most of the functionality is executed within the game
playArea.Clear(Color.White);
}
private void btnStart_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
}
It appears you have combined inheritance and composition in an impossible sort of way. The base Form1 type has a field declared to be of the derived Bat type. In addition, it uses a field-initializer to initialize it to a new instance of that type. Clearly, you have a turtles-all-the-way-down issue: when you create a Bat (or a Form1 for that matter), the field-initializer will run - this will create an instance of another Bat, which in turn will create yet another Bat, and so on, ad-infinitum in theory. (in practice: until you run out of stack-space).
Here's a simple fix that should solve the stack-overflow issue, but may not be the most appropriate design in the 'big-picture':
public class Bat
{
public void Draw(Graphics playArea)
{
...
}
}
Notice how this type no longer subclasses Form1; it inherits directly from System.Object. Now neither the Form1 nor the Bat classes will exhibit infinite recursion when instances of them are created.
It's hard to suggest the best fix without knowing the ultimate aim here. I suggest you give some thought to the best way to design these classes. I would think you need to spend some time learning about the C# programming language, OO design, as well as WinForms specifics. I think you're actually looking to override the OnPaint virtual method here.
Often the cause is confusing a property with its backing variable.
Something along the lines of:
public class tmp
{
private int _x;
...
public int X(x)
{
X = x;
}
You have a simple problem.
your class Bat is derived from Form1, and in Form1 you create a new instance of Bat, which in turn is based on Form1 so that creates a new instance of Bat.... and so it repeats until your stack space is used up.
In general, Form1 probably should not know about the class Bat, and that all code that needs to know about Bat should be in the Bat class. However, in exceptional circumstances, you can solve this issue like this:
partial class Form1
{
public Form1(Bat _bat)
{
mBat = _Bat;
}
protected Bat mBat;
}
and on the Bat class
public class Bat : Form1
{
public Bat() : base(this)
{
}
}

Categories