in my game, I got some teachers to make the player increase his skills. At the moment, there are 12 guys, so I wrote a base class for them.
This class selects the correct data from its own "teacherDataClass". The data is set by an index from the Editor.
My code:
[SerializeField]
int teacherIndex; // set an index in the editor -> selection of teacher
NpcTeacherData teacherData; // the data class
private void Start()
{
NpcTeacherData[] teachers = // Collection of all teachers
{
new TeacherAlchemist(),
new TeacherBlacksmith(),
new TeacherBowyer(),
new TeacherButcher(),
new TeacherHunter(),
new TeacherInnkeeper(),
new TeacherJuggler(),
new TeacherMessenger(),
new TeacherPriest(),
new TeacherTamer(),
new TeacherThief(),
new TeacherTownGuard()
};
teacherData = teachers[teacherIndex]; // Get the right teacher by the index
}
So this looks fine and this works fine. If you do not want to use the Editor, you could compare the tag of the teacherObject like this way
NpcTeacherData teacherData; // the data class
private void Start()
{
switch (gameObject.tag) // compare the tag of the teacher and set its class
{
case "TeacherAlchemist":
teacherData = new TeacherAlchemist();
break;
case "TeacherBlacksmith":
teacherData = new TeacherBlacksmith();
break;
//...
}
}
But I do not like these ways that much :/ Are there any better possibilities? Like a DropDownSelection in the Editor maybe?
I tried
[SerializeField]
object script; // Place the right data class here
[SerializeField]
object script as NpcTeacherData; // Place the right data class here
But this did not work.
I just want to optimize it :)
Maybe you can use an enum to achieve this?
It would be something like this:
public enum TeacherType
{
TeacherAlchemist,
TeacherBlacksmith,
TeacherBowyer,
TeacherButcher,
TeacherHunter,
TeacherInnkeeper,
TeacherJuggler,
TeacherMessenger,
TeacherPriest,
TeacherTamer,
TeacherThief,
TeacherTownGuard
}
public TeacherType type;
private NpcTeacherData teacherData;
private void Start()
{
switch (type)
{
case TeacherType.TeacherAlchemist:
teacherData = new TeacherAlchemist();
break;
//...
}
}
However it doesn't differ a lot from your first solution (only choice is a bit easier since the type is shown instead of an index).
Hope this helps,
You can use Editor GUI Popup. Here is how you can make a dropdown/popup -
public string[] options = new string[] {"TeacherAlchemist","TeacherBlacksmith","TeacherBowyer","TeacherButcher","TeacherHunter","TeacherInnkeeper","TeacherJuggler","TeacherMessenger","TeacherPriest","TeacherTamer","TeacherThief","TeacherTownGuar"};
public int selectedIndex = 0;
Then Use OnGUI function to store selected value
void OnGUI()
{
selectedIndex = EditorGUILayout.Popup("TeacherDataClass", selectedIndex, options);
}
You can find reference here.
Related
I've recently taken on the task of custom properties in Photon. I have been able to figure out how to set the custom properties, but not get the custom properties. My hashtable is in my player controller script, while the place where I set (and where I want to get) properties is in a round loop script.
From RoundSystem:
private IEnumerator TeamBalance()
{
angelCount = Mathf.Floor(PhotonNetwork.PlayerList.Length * angelPercent);
currentAngels = angelCount;
currentPlayers = PhotonNetwork.PlayerList.Length;
foreach (var item in PhotonNetwork.PlayerList)
{
var itemPhotonView = (PhotonView)item.TagObject;
itemPhotonView.RPC("SetPlayerTeam", item, citiString);
}
for (int i = 0; i < angelCount;)
{
var item = PhotonNetwork.PlayerList[Random.Range(0, PhotonNetwork.PlayerList.Length)];
var itemPhotonView = (PhotonView)item.TagObject;
if (/* random player selected's, AKA, item's team == citiString */)
{
itemPhotonView.RPC("SetPlayerTeam", item, angelString);
i++;
}
}
yield return null;
//the reason this is in an IEnumerator with 'yield return null'
//is because I plan to add a waiting period once I figure this out
//it's for the game loop
}
From PlayerController:
[PunRPC]
public void SetPlayerTeam(string teamString)
{
//in the class: private ExitGames.Client.Photon.Hashtable playerProperties;
if (!playerProperties.ContainsKey("team"))
{
playerProperties.Add("team", teamString);
}
playerProperties["team"] = teamString;
PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
}
At the beginning of the round, a percentage (in this case 1/3) of players are chosen to be an "angel". The check here is needed because in cases of multiple angels, you don't want an already existing angel to count as a new change. (Also, it's probably important to known generally how to get custom properties if I'm going to be using them.) If I don't include the check in RoundSystem, the outcome is 2 citizens and 1 angel (in a test with 3 players). Also, if you see any spaghetti code that could be improved on, please don't hesitate to tell me. :)
Use Player.CustomProperties dictionary to access player's custom properties.
foreach (var item in PhotonNetwork.PlayerList)
{
if (item.CustomProperties.ContainsKey("team"))
{
Debug.Log(item.CustomProperties["team"]);
}
}
Also, the RoundSystem can implement IInRoomCallbacks interface and listen to OnPlayerPropertiesUpdate to catch the exact moment when the team gets updated. https://doc-api.photonengine.com/en/pun/v2/interface_photon_1_1_realtime_1_1_i_in_room_callbacks.html
I've read a lot of topics on this issue but I'm not finding an answer. I'm fairly new to this so please bear with me.
I'm trying to pass values from datagridview to a list. And then in a new class I want to make som methods accessing that list. Trouble is that when I pass the datagridview it returns it without content and values which means I can't do anything with it.
The code under ////TESTING//// works like I want. I create an instance of the specified list and it's counting the amount of rows properly, see screenshot.
public List<vertEl> getVertList = new List<vertEl>();
//Opens the file dialog and assigns file path to Textbox
OpenFileDialog browseButton = new OpenFileDialog();
private void browse_Click(object sender, EventArgs e)
{
browseButton.Filter = "Excel Files |*.xlsx;*.xls;*.xlsm;*.csv";
if (browseButton.ShowDialog() == DialogResult.OK)
{
//SOME CODE TO GET DATA FROM EXCEL AND SOME METHODS TO CALCULATE
//VALUES TO PASS TO THE TAB VERTIKALELEMENTER TAB IN MY DATAGRIDVIEW
//VERTIKALELEMENTER IS vertElementerDgv IN MY CODE
////TESTING////
GetVertElementasList TEST = new GetVertElementasList();
getVertList = TEST.vertList(vertElementerDgv);
MessageBox.Show(getVertList.Count.ToString());
}
else return;
}
I now want to do this in a seperate class and call a method from that class to do the same but when I try that with code underneath I do not get the same count as when I have the code in form1 (public partial class BridgeGeometry). It return count of 0. The method foo() is assigned to the button 1 in the form.
class GetKoord
{
public GetVertElementasList getList = new GetVertElementasList();
BridgGeometry obj = new BridgGeometry();
public void foo()
{
var TEST = getList.vertList(obj.vertElementerDgv);
//var TEST = obj.getVertList;
MessageBox.Show(TEST.Count.ToString());
}
}
I also tried to get the values directly from the datagridview but there's nothing in it when I access it from a class which is not the form1/BridgeGeometry class.
Form - screenshot
You could run a loop and store the information with selected rows into a public var with something like this:
string itemOne = dataGridView1.SelectedRows[0].Cells[1].Value + string.Empty;
string itemTwo= dataGridView1.SelectedRows[0].Cells[2].Value + string.Empty;
string itemThree = dgMasterGridBun.SelectedRows[0].Cells[3].Value + string.Empty;
Variables
public var varItemOne = itemOne;
public var varItemTwo = itemTwo;
public var varItemThree = itemThree;
Based on the link I managed to get this working. Probably not the best solution, but a working one. I tried to wrap my head around databinding, listbinding etc. but since the class with the input values are a messy one I gave that up for now. The datagriview input values are a little from lists and some from other datagridview.
MSDN-forum: Accessing Form1 controls from a different class
Explanations are given in the link so I'll just provide how I did it in my program.
If my GetKoord class are like this:
public class GetKoord
{
private BridgGeometry bridgeGeometry;
public GetKoord(BridgGeometry form1)
{
bridgeGeometry = form1;
}
public List<vertElementerParam> getListvertElementer(List<vertElementerParam> theList)
{
//var vertElementerDgv = bridgeGeometry.vertElementerDgv;
GetVertElementasList getVertElementasList = new GetVertElementasList();
List<vertElementerParam> MyDgvListList = new List<vertElementerParam>();
MyDgvListList = getVertElementasList.vertList(bridgeGeometry.vertElementerDgv);
//MessageBox.Show(MyDgvListList.Count.ToString());
theList = MyDgvListList;
return theList;
}
}
then I can get the list in Button1_Click like this, check the screenshot in the first post:
public List<vertElementerParam> getVertList = new List<vertElementerParam>();
private void button1_Click(object sender, EventArgs e)
{
GetKoord getKoord = new GetKoord(this);
List<vertElementerParam> testList = new List<vertElementerParam>();
testList = getKoord.getListvertElementer(getVertList);
MessageBox.Show(testList.Count.ToString());
}
I am trying to create a menu system and i am storing the menus in stacks once they have already been visited. Im trying to use Stack.Peek() to basically say: if menuName = menuStack.Peek, then continue.
menus have a drawRectangle, sprite, and Menuname enumeration associated with them, and all menus are child classes of the Menu class.
public static void GoToMenu(MenuName menuName)
{
Stack<Menu> menuStack = new Stack<Menu>();
Stack<Menu> tempStack = new Stack<Menu>();
if(menuStack.Peek() = MainMenu){
}
}
More or less, if menuStack.Peek returns a mainMenu object. How do i check that?
i just really dont know how to read menuStack.Peek(). I dont know how to apply it to an if statement to check if it equals a mainmenu object, a pausemenu object or whatever.
public static void GoToMenu(MenuName menuName)
{
Stack<Menu> menuStack = new Stack<Menu>();
Stack<Menu> tempStack = new Stack<Menu>();
if(menuStack.Peek().Name == menuName){
menuStack.Pop();
}
}
that is what i needed
Context: I am trying to make a little game of Backgammon in Windows.Forms. I have 3 classes GameBoard.cs, Points.cs and Checkers.cs (as well as Form1.cs). What I need to be able to do is change values of some properties of a PictureBox using some of my other classes.
Specifically I have this code:
// gameBoard.cs
namespace backgammon
{
public class gameBoard
{
Checker checker1;
Points point1;
Points[] pointsArray;
public gameBoard()
{
// make new checker (ID, PictureBox, startingPoint)
checker1 = new Checker(1, checkerPicBox1, 1);
// make new Point (ID, arrayOfCheckers)
point13 = new Points(1, new Checker[]{checker1 /*,checker2... etc*/});
pointsArray = new Points[MAX_POINTS];
pointsArray[0] = point1;
}
}
}
so this is how I have "setup" the checkers and points. My checker and point classes can get and set on all variables that were passed into its construction.
Problem: what I am trying to achieve is to "highlight" a checker once it is clicked.
In Form1:
// Form1.cs
private void checkerPicBox1_Click(object sender, EventArgs e)
{
int pointNumber = gameBoard.checker1.getPointMember();
// find the top most checker in the checker array so we can highlight it
Checker topMost = gameBoard.pointsArray[pointNumber - 1].getCheckerFromIndex(gameBoard.pointsArray[pointNumber - 1].getCheckerArray().Length - 1);
// get the picturebox and change the image
topMost.getPictureBox().BackgroundImage = global::Backgammon.Properties.Resources.blackCheckerSelected;
}
The code compiles and runs but when it gets to the last line of checkerPicBox_Click nothing seems to happen (the image doesn't change).
What is happening here? Do I not have the correct instance of my PictureBox? Or am I doing this in a weird way/not the way i'm supposed to be?
Since the checkers are connected to the pictureboxes, why not create a new reference property for them in the Checker class?
You could pass the form containing the pictureboxes in the constructor, and use the form to create your references to the right picturebox instances.
// gameBoard.cs
namespace backgammon
{
public class gameBoard
{
Checker checker1;
Points point1;
Points[] pointsArray;
public gameBoard(Form gameForm)
{
// make new checker (ID, PictureBox, startingPoint)
checker1 = new Checker(1, gameForm.checkerPicBox1, 1);
// make new Point (ID, arrayOfCheckers)
point13 = new Points(1, new Checker[]{checker1 /*,checker2... etc*/});
pointsArray = new Points[MAX_POINTS];
pointsArray[0] = point1;
}
}
}
Add the picturebox property to your Checker class:
// checker.cs
public class Checker
{
PictureBox _picturebox;
//... other code here
public Checker(int ID, PictureBox picturebox, Points startingPoint)
{
_picturebox = picturebox;
//...other code here
}
}
Then we can use this in the game form:
gameBoard gameBoard1 = new gameBoard(this);
gameBoard1.checker1._picturebox.BackgroundImage = global::Backgammon.Properties.Resources.blackCheckerSelected;
gameBoard1.checker1._picturebox.Invalidate();
For example i have a player object with a list of suits of cards it has.
player.card[0] = "diamonds";
player.card[1] = "clubs";
player.card[2] = "spades";
etc...
also i have 4 hidden pictureboxes with an image of suites ( pb_spades , pb_hearts m etc. )
and another 4 pictureboxs ( pb_playerCard1 , pb_playerCard2 , etc. ) to which I have to assign an image from a hidden pb corresponding to the suit of card the player object has.
Therefor
if ( player.card[0] == "diamonds" ) { pb_playerCard1.Image = pb_diamonds.Image; }
of course, doing it all with IFs would take quite a long time... Can I somehow use variable value as a part of an objects name?
kinda
for (int i = 1; i != 5; i++)
{
pb_playerCard+'i'.Image = pb_+'player.card[i+1]'.Image;
}
I don't think that you can use a value as a part of the control name. But, one can use an aray of controls. You will have to change many declarations and initializations of your picture boxes and put them into an array, but you will be able to write more descriptive and readable code.
Create a class Suite that has all properties, like this:
class Suite {
public string Name { get; set; }
public Image Image { get; set; }
and then create a static object for each color:
public static Diamonds = new Suite { Name = "Diamonds", Image = Resources.DiamondImage };
// ...
}
Now you can use Suite.Diamonds.
Even better is to use a Flyweight pattern to avoid the static fields. You use the Flyweight to implement the Card class.
First, there's no reason to have a hidden PictureBox control just so you can use it's Image property to store an image. Just create Image objects.
You could store the images in a dictionary, indexable by name:
var cards = new Dictionary<string, Image>() {
{ "diamonds", Image.FromFile("diamonds.jpg") }
{ "clubs", Image.FromFile("clubs.jpg") }
//...
};
Then instead of this:
if ( player.card[0] == "diamonds" ) { pb_playerCard1.Image = pb_diamonds.Image; }
You would write:
pb_playerCard1.Image = images[player.card[0]];
This code is still not great (any time you see variables like foo1, foo2, foo3, you should be putting those in an array so they can be indexed by number). The next step might be to refactor the code so you have something like:
pb_playerCard[0].Image = images[player.card[0]];
Or:
pb_playerCard[0].Image = player.card[0].Image;
thanks again guys, I got it working using a List to store card suites for the player objects and a dictionary that stores image references.
Dictionary<CardType, System.Drawing.Image> newDeckImages = new Dictionary<CardType, System.Drawing.Image>();
...
newDeckImages.Add(CardType.Diamonds, pb_diamonds.Image);
newDeckImages.Add(CardType.Hearts, pb_hearts.Image);
newDeckImages.Add(CardType.Clubs, pb_clubs.Image);
newDeckImages.Add(CardType.Spades, pb_spades.Image);
...
private void showMyCards()
{
pb_termCard1.Image = newDeckImages[Terminator.cards[0]];
pb_termCard2.Image = newDeckImages[Terminator.cards[1]];
pb_termCard3.Image = newDeckImages[Terminator.cards[2]];
pb_termCard4.Image = newDeckImages[Terminator.cards[3]];
}
The main class Player and the associated enumeration should be like this:
class Player
{
public Player(int n)
{
Cards = new List(n);
}
public IList Cards { get; private set; }
}
enum CardType
{
Diamond,
Club,
Spade,
Heart
}
Another method GetPictureBox() should in the winform partial class
public PictureBox GetPictureBox(string pictureBoxName)
{
if(this.Controls.ContainsKey(pictureBoxName))
{
Control control = this.Controls[pictureBoxName];
PictureBox pictureBox;
if ((pictureBox = control as PictureBox) != null)
{
return pictureBox;
}
}
return null;
}
Use this method in the class using the player instance.
Player player = new Player(3);
player.Cards.Add(CardType.Diamond);
player.Cards.Add(CardType.Club);
player.Cards.Add(CardType.Spade);
Dictionary dictionary = new Dictionary();
dictionary.Add(CardType.Diamond, pb_diamonds);
dictionary.Add(CardType.Spade, pb_spades);
dictionary.Add(CardType.Club, pb_clubs);
dictionary.Add(CardType.Heart, pb_hearts);
Finally the assignment to the image property,
for (int i = 1; i < 5; i++)
{
string playercardPictureBoxName = string.Concat("pb_playercard", i.ToString());
PictureBox pictureBox = this.GetPictureBox(playercardPictureBoxName);
if (pictureBox != null)
{
pictureBox.Image = dictionary[player.Cards[i - 1]].Image;
}
}
No, not how you're doing it, and although similar things can be achieved with reflection, what you want here is probably closer to just using FindControl for the "pb_playerCard" part and a simple dictionary of type Dictionary<CardSuitEnum, WhateverTheTypeOfImageIs> which you can populate with the pb_diamonds.Image etc.
Apols, I don't know what type .Image holds and can't be bothered to look it up :)