I'm a University Student who has experience in C++ and is new to C#. I wanted some feedback about how to approach building a C# Form application for a 5x5 Tic Tac Toe Game.
The specifics of the program require that a user enters a time for the player to make a move choice. Then the player selects whether they or the computer will go first. Then the player clicks a start button for the game to commence.
So far I figured I'd created a 'Move' class with a X and Y Coordinate representing a location on a game grid, and a 'Player' class that contains a list of 'Move' objects that they have made in the game. I was planning on adding a grid of clickable labels that would add a move to the players list of moves. As for the AI, I think it would be pretty easy to create an algorithm to block the human player from completing a row, but I'm not sure what data structure would be most efficient for this. Perhaps I'm thinking about it all wrong to begin with.
Any thoughts?
In the UI, I will use a 5 * 5 TableLayoutControl with a Button in each cell. You can set the FlatStyle for these button to Flat for the look.
Now, as far as maintaining a model is concerned, I will just have a single class to represent players. Something like this:
class Player : INotifyPropertyChanged
{
List<Point> _selectedCells = new List<Point>();
public List<Point> SelectedCells
{
get
{
return _selectedCells;
}
set
{
if (value != _selectedCells)
{
_selectedCells = value;
RaisePropertyChange(new PropertyChangedEventArgs("SelectedCells"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChange(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
Now, there will be 2 instances of this class. One for human and one for computer. On button click, you can set SelectedCells property. Now, changing this property will raise PropertyChanged event. a handler can be placed that checks for the victory.
If user has not won yet, generate computer move and set same property on computer instance. Again it will check for victory.
As and when a user wins, you can display appropriate message. You can add a string property Name to Player class to ease the victory message display mechanism.
Related
I am making a game using the new input system, but I have gotten stuck when trying to have a button do two things.
Currently, I have an action map for gameplay, and the mouse click acts as the "fire" event.
When the player clicks on a UI element (for example, an inventory slot), I don't want the "fire" event to occur, but rather another suitable method.
I tried to use two action maps (gameplay and UI), but I don't want the player to stop moving (due to gameplay input not being picked up on the UI action map).
I'm sure there is a simple solution to this, but there is a lack of extensive information on the new input system. I am thinking I may have to add an IPointer OnEnter, OnExit interface to the monobehaviour script and have the input script decide which action is best suited at the time of being pressed. Is there a more efficient way to do this?
Put the responsibility on the Firing script to check that player state is valid. You can expose player state information in order to check if the player is in a menu or in some other state that does not allow Fire to happen.
CanFire is a property in the player state composed from any number of checks.
public class PlayerState
{
.. other properties like IsInMenu, IsInCutscene, IsDead, etc..
public bool CanFire => !IsInMenu && !IsInCutscene && !IsDead;
}
Then in your firing script when the event is fired, check the CanFire boolean in the player state.
if (playerState.CanFire) Fire();
I really need to add the possibility of swiping images like in dating apps (Tinder maybe) in my own app. If the image is swiped to the left, then a certain value should be assigned to the variable (for example, +1). If to the right, then nothing should change (+0 to the variable). After swiping the image, the next image should float smoothly (from the front, from the bottom, it doesn't matter).
I tried to do it myself, but there are no ideas how this can be done. I understand that it will be more difficult to do this on Windows Forms than on WPF. I have only recently started to be interested in WPF, so solving this problem on WPF would also be useful, but Windows Forms is still a priority. Please help me solve this issue.
You forgot to define "swipe". Winforms doesn't have the notion of finger input, only the notion of mouse drag.
Do you want, that if the operator drags the mouse to the left that the image moves with it? Is a small drag enough, or should the operator drag the image completely outside the window?
What should happen if the operator drags a small part, but stops dragging? Should the image move back as if there was no drag? Or should the image stay dragged halfway?
Model
You used the word Image, but in fact the images stands for something more: in Tinder it stands for the person behind the image, a name, a birthdate, a description, and other parts, among which an image.
Let's call this a Profile: every profile has several properties, among others an Image.
class Profile
{
public Image Image {get; set;}
...
}
In your model you will need a FIFO sequence of "Profiles to be shown", a collection of rejected Profiles and a collection of accepted Profiles. You didn't say what you wanted to do with the rejected and accepted Profiles, so all I do is put the Rejected Profiles in a Repository, and the accepted ones in a different Repository.
What happens in the repository is hidden for the model. It might be that you delete everything, or you save it in a file, or a database, or whatever, your Model doesn't have to know. All it has to know is that both repositories need to have an interface to put the Profiles in:
interface IProfileRepository
{
void Add (Profile profile);
}
The repository with the rejected images will probably just throw the Profile away, while the other repository might do things like notify the owner of the Profile that he has been accepted.
We also need some input Profiles. I also don't know where they come from:
interface IProfileSource
{
Profile GetProfile(); // returns the next Profile
}
The actual ProfileSource might read the data from an XML file, or from the internet, or whatever, this is outside the question.
So in you program you will have the following:
class ProfileModel
{
private IProfileSource ProfileSource {get;} = new ...;
private IProfileRepository AcceptedProfiles {get;} = new ...;
private IProfileRepository RejectedProfiles {get;} = new ...;
public Profile GetNextProfile()
{
return ProfileSource.GetProfile();
}
public void AcceptProfile(Profile profile)
{
AcceptedProfiles.Add(profile);
}
public void RejectProfile(Profile profile)
{
RejectedProfiles.Add(profile);
}
View
The form that will display the images of the Profile will need a UserControl that will show a Profile. It is hidden what is shown of the Profile. You will probably only show the Image, but if you want, you can let it show the Age of the person, or the Name, Location, etc. All that your program knows is that you can ask the ProfileControl to show a Profile, what is shown, and how, is up to the ProfileControl.
Use visual studio to create a new UserControl, named ProfileControl. Use Visual Studio designer to draw on the control what you want to show when a Profile needs to be shown. If you only want to show the Image, add a PictureBox to the ProfileControl and let it dock. If you also want to show the Name, add a Label, etc
class ProfileControl : UserControl
{
private Profile profile;
public ProfileControl()
{
InitializeComponents();
}
public Profile Profile
{
get => this.profile;
set
{
if (this.Profile != value)
{
this.profile = value;
this.pictureBox1.Image = this.profile.Image;
}
}
}
}
Consider to add an event ProfileChanged and a protected method OnProfileChanged, to notify others that this ProfileControl shows a new Image.
You will need another UserControl that will do the dragging of the ProfileControl. It will have two ProfileControls: the current one and the next one. Upon MouseDrag the location of the current ProfileControl and the next ProfileControl will change. The next ProfileControl will be adjacent to the current one, depending on the direction of the drag.
This SwipeControl hides how the swiping is done. Users of the SwipeControl (= software, not operator), will only set the current and the next Profile, and it gets notified whenever the current profile is accepted or rejected via events. The event will automatically set the Next profile (if there is one)
Use the visual studio designer to give the SwipeControl two ProfileControls. Add events handlers for events:
MouseDown: remember current mouse position as DragStartPosition. Give CurrentProfileControl and NextProfileControl the size of the ClientArea of the SwipeControl. Set the Location of the CurrentProfileControl to (0, 0), so it is in the upper left corner of the ClientArea of the SwipeControl. NextProfileControl is still not visible, we don't know whether the operator will swipe to the left or to the right.
MouseMove: the horizontal distance that the mouse travelled = current mouse position X - DragStartPosition X. Shift the X location CurrentProfileControl with this Distance travelled. Decide whether NextProfileControl should be on the left or on the right side of CurrentProfileControl. Calculate the Location. Make NextProfileControl visible.
MouseUp: If Distance Travelled is more than some minimal, then set the swipe complete, otherwise undo: dock current and make next invisible.
SwipeComplete: if Accepted raise event ProfileAccepted, if Rejected raise event ProfileRejected. The Profile in the NextProfileControl is set to CurrentProfileControl. Fetch the NextProfile and put it in the NextProfileControl
class SwipeControl : CustomControl
{
public Profile CurrentProfile
{
get => this.CurrentProfileControl.Profile;
set => this.CurrentProfileControl.Profile = value;
}
public Profile NextProfile
{
get => this.NextProfileControl.Profile;
set => this.NextProfileControl.Profile = value;
}
public event EventHandler ProfileAccepted;
public event EventHandler ProfileRejected;
protected virtual void OnProfileAccepted()
{
// raise event ProfileAccepted
this.ProfileAccepted?.Invoke(this, EventArgs.Empty);
}
Use visual studio designer to add the event handlers and implement the code as written.
##The SwipeForm##
Use visual studio designer to add the SwipeControl to the SwipeForm. Also add the Model.
Subscribe to the Accepted / Rejected events of the SwipeControl.
Upon load of the form: get the first and the next Profile from the model and put them in the SwipeControl
Upon event ProfileAccepted: get the CurrentProfile from the SwipeControl and put it in the model as Accepted. The nextProfile will be the current one. Get the next from the model and set this as next profile in the SwipeControl.
Do something similar with event ProfileRejected
I'm Working on a game, and i want to create a mute button. So i wrote a script for that and i attach this script into a gameObject that don't destroy on load.
I link the script with a UI Button present in a main menu. This UI button is destroy when i'm changing scenes.
when i start my game, and i click on the button, the audio turns off, and when i click back, it turns on.
But when i change to another scene, and i go back to the main menu, my script doesn't have the UI Button attach to it. so when i touch the button the audio doesn't change is behavior
I would like to know if it's possible to maintain the link between the UI Button and the script (attach to a normal GameObject), even if the UI Button is destroyed?
i tried this:
ButtonGameObject = GameObject.Find("UI Button");
but it doesn't work.
How can i fix that?
Thanks a lot.
There are many ways to work around this, but here's one:
Step 1: If you haven't already done so, implement a weak singleton pattern on your mute script (let's call it MuteScript.cs for now).
private static MuteScript singleton {get; private set;}
private void Awake() {
if (singleton == null) singleton = this;
[whatever other stuff you were already doing in Awake()]
}
public static void ToggleMute(Graphic graphic)
{
singleton._ToggleMute(graphic);
}
private void _ToggleMute(Graphic graphic)
{
[whatever code you were running in your original mute method]
}
Step 2: Attach a simple script to your UI button:
public class MuteButton: MonoBehaviour
{
Graphic myGraphic;
private void Awake() {
myGraphic = GetComponent<Graphic>();
}
public void OnClick() {
MuteScript.ToggleMute(myGraphic);
//I assume you want to do something like change the colour of the button when
//the player toggles it. Passing the Graphic to the MuteScript is the easiest
//way of doing this. If you really want to keep your code clean, though,
//I recommend expanding the MuteButton class with methods to take care of
//the UI side of things.
}
}
Step 3: In Unity Editor, setup your button to call its own OnClick() method, not the MuteScript method.
-
Now when you click the button, it will call the static MuteScript.ToggleMute(), which accesses the static-cached singleton reference, which in turn points back to your original object.
Singletons and static accessors are great for efficiency in Unity because they save you from having to call expensive search functions like FindObjectsOfType(). The only gotcha is that you have to be careful about not having multiple copies of a singleton-class object lying around, especially when using DontDestroyOnLoad().
So a better approach rather than having a script search and grab the component is to use a PlayerPrefs class from Unity. Essentially, it will hold onto all important aspects of the game and auto fill information.
This is a great tool for a lot of user customization aspects of a game. When using this, have a script (sceneController would be a good name) that will run when the scene starts (create a blank object at 0,0,0) and then under void Start() have the script grab the mute/unmute button: GameObject.Find("MuteButton") or (my favorite) give it a tag called MuteButton and run: GameOject.FindWithTag("MuteButton"). Also once you get a link to the button, add a listener to it for when the button is pressed.
Also store the sound manager in a gameController that will be passed throughout the game. This would control the soundManager and have access to that. So sceneManager will also need a reference to gameManager.
Using a script that is just for player preferences (a controller if you will) is a better way to organize and contain any preferences for the users. Just better clarity and separation.
Example
SceneController Object Script
class SceneController {
GameObject muteButton;
void Start() {
muteButton = GameObject.FindWithTag("muteButton");
muteButton.AddListener(muteButtonCheck);
}
void muteButtonClick() {
if (PlayerPrefs.GetInt("playerMute")) {
// If 1 (on)
// Set sound off
PlayerPref.SetInt("playerMute", 0);
} else {
// It's 0 (off)
// Set sound on
PlayerPrefs.SetInt("playerMute", 1);
}
}
}
I have three buttons, when you tap one button, a panel with a text appear.
The problem is when I attached the script, nothing happens.
The "click" is registered but the the panel never appears.
My script is attached to each of the button and is something like this:
public GameObject panel; //i use to put the panel in unity
bool selected = false;
void Start () {
panel.SetActive(false);
}
void OnSelect() {
selected = !selected;
panel.SetActive(true);
}
I probably have to do something else with the panel but I can't figure it out.
Do it like this:
(1) Add the canvas to your project
(2) BIG TIP - be sure to select Scale with screen size.
That's the only one you ever use. Unity accidentally set the wrong default there, they have not fixed it yet.
(3) In your Canvas, add a BUTTON Make it say perhaps "Test"
(3) In your Canvas, add another BUTTON Make it say perhaps "Another Test"
(4) Make a script something like this...
public class MainScreen:MonoBehaviour
{
public void UserClickedTest()
{
Debug.Log("test..");
}
public void UserClickedAnotherTest()
{
Debug.Log("another test..");
}
}
(5) put ONE copy of that script on ANY object you like. You can put it on your camera, on the canvas, or anywhere else that makes sense
For now let's say you put it on your CAMERA object, for example.
(6) Click on the button "Test" .....
And do this ...
click the PLUS button under OnClick
you see the slot that says "_main" in this example. DRAG your CAMERA item from HEIRARCHY, to that slot
Using the drop down menu:
select the "UserClickedTest()" function ...
good eh?
Now for the other button, do the same but select the "UserClickedAnotherTest()" function.
You're done! Run and test!
You can't use the OnSelect system unless you use ISelectHandler and more stuff: it is difficult for beginners. I strongly recommend the OP masters the simpler technique I explain here. Enjoy!
Maybe you attached the script to the panel. If so, your scripts can not be executed as long as your GameObject is SetActive(false).
I hope I was able to help you.
This has been bothering me for quite some time so here we go...
I'm making a 2D game using XNA framework and part of the game needs the usage of mouse click funtions in UI. By using these textures as buttons it makes another window to pop up with more functions. Now the problem is when you create a button and use it, it will open another window with more buttons in it. Because these 2 buttons on different windows are at the same location, that one mouse click operates both buttons and makes the action of the last button instantly rather than having to click twice.
The real question is how do I use sprites as buttons rather than checking the position and checking if that possition is clicked?
Thanks in advance for any comments.
I created a basic UI framework that has Windows with Controls on them. Windows are stacked by zOrder as are the controls on each window. There is a ScreenManager class that updates every tick that tracks the mouse. When it sees a button click it walks the window stack from top to bottom looking for the first window that contains the click coordinates. It then calls the OnClick sub of that window instance, which does the same thing for the controls on the window. Each method stops walking the control stack as soon as it finds the first control that contains the click coords.
Each unique window in my game is a subclass of that basic window class that creates child control instances and lays them out in the specific way for it's layout. Controls like buttons raise a clicked event that the window subclass handles to do the logic needed for that button.
You could use a state manager in which the proper UI live.
In your case, the first state would have your first buttons, lets call it FirstGameState. When you click on them, the state machine gets you to a another state, lets call it MenuState. You can implement your state manager in a way that allows you to decide who handle events and who doesn't. The only state that handles your click events would be the one flagged to do so. The rest would only draw and update the visual content but handle no user events.
Here is an example for you:
namespace States
{
class StateManager
{
List<GameState> _states = new List<GameState>();
List<GameState> _statesCopy = new List<GameState>();
public Game Game { get; private set; }
public SpriteBatch SpriteBatch { get; private set; }
public StateManager(Game game, SpriteBatch sb)
{
Game = game;
SpriteBatch = sb;
}
public void AddState(GameState state)
{
_states.Add(state);
}
public void RemoveState(GameState state)
{
_states.Remove(state);
}
public void Update(GameTime gameTime)
{
_statesCopy.Clear();
foreach (GameState state in _states)
if (state.RequireUpdate)
_statesCopy.Add(state);
foreach (GameState state in _statesCopy)
if (state.RequireUpdate)
state.Update(gameTime);
}
public void Draw()
{
foreach (GameState state in _states)
state.Draw();
}
}
}