How do I close a GUI-menue when clicking outside of it?
What would be the smartest way to do it?
Here a picture of a two-stage GUI example:
And the belonging code, feel free to use it for your own purpose:
using UnityEngine;
using System.Data;
using Mono.Data.SqliteClient;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using UnityEngine.EventSystems;
public class menue2 : MonoBehaviour
{
private Vector2 scrollViewVector = Vector2.zero;
static int menueWidth = 125;
static int menueHeight = 25;
Rect dropDownRect1 = new Rect(0,0,menueWidth,menueHeight);
Rect dropDownRect2 = new Rect (menueWidth, menueHeight, 170, menueHeight);
// Menue Level 1
public static string[] list = {"Navigation","Setup Ordner", "Optionen Ordner", "Setup Dateien", "Optionen Dateien", "blabla"};
// Menue Level 2
public static string[] camSetup = {"Kamera Zoom Min.", "Kamera Zoom Max.", "Kamera Geschw. Normal", "Kamera Geschw. Schnell"};
public static string[] setupOrdner = {"Farbe = ","Höhe = ","setupFolder3","setupFolder4","setupFolder5","setupFolder6"};
public static string[] optionenOrdner = {"setupFolder1","setupFolder2","setupFolder3","setupFolder4","setupFolder5","set upFolder6"};
public static string[] setupDatei = {"oooooo","ppp","üüüüüüüüü","qqqqqq","nnnnnnn","mmmm"};
public static string[] optionenDatei = {"wwwww","eeeee","rrrrr","tttt","zzzzzzz","uuuuuu"};
public static string[] blabla = {"aaaaa","ssssss","gggg","ddddd","jjjjjj","hhhhh"};
public static string[][] listCollection = {camSetup, setupOrdner, optionenOrdner, setupDatei, optionenDatei, blabla};
int indexNumber;
int indexNumber2;
int level = 0;
string lastClicked = null;
public GameObject other;
void OnGUI()
{
if (GUI.Button (new Rect ((dropDownRect1.x), dropDownRect1.y, dropDownRect1.width, dropDownRect1.height), "Setup")) {
if (level == 0) {
level = 1;
lastClicked = "Setup";
} else if (level > 0)
level = 0;
}
if (level >= 1) {
for (int index = 0; index < list.Length; index++) {
if (GUI.Button (new Rect (0, (index * 25 + dropDownRect1.height), dropDownRect1.width, dropDownRect1.height), list [index])) {
indexNumber = index;
level = 2;
lastClicked = (list [index]);
}
}
if (level == 2 || level == 3) {
for (int index = 0; index < camSetup.Length; index++) {
if (GUI.Button (new Rect (menueWidth, ((indexNumber * menueHeight) + (index * menueHeight) + menueHeight), menueWidth, menueHeight), listCollection [indexNumber] [index])) {
indexNumber2 = index;
level = 3;;
lastClicked = (listCollection [indexNumber] [index]);
}
}
}
}
if (Input.GetMouseButtonUp (0)) {
print (lastClicked);
}
}
}
Thanks a lot for any help!
I stayed with scripting my GUI in the end and found a quite simple solution.
void closeMenue()
{
Vector2 mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
if (Input.GetMouseButtonDown (0) && mousePosition.x > Screen.width/2 || Input.GetMouseButtonDown (0) && mousePosition.y < Screen.height/2)
{
level = 0; // closing everything
}
}
So I simply check for the cursor position.. here adjusted to a menue on the top left.
But the allowed area can easily be changed to the personal situation of course.
Related
I am very new to unity and I made a snake game. However, the apple can sometimes spawn inside the snake. I'm assuming that this needs an if statement like if CreateRandomApple = TailNode then try again but I'm not sure how to code that. Here is my code for the snake and the apple.
void PlacePlayer()
{
playerObj = new GameObject("Player");
SpriteRenderer playerRender = playerObj.AddComponent<SpriteRenderer>();
playerSprite = CreateSprite(playerColor);
playerRender.sprite = (playerSprite);
playerRender.sortingOrder = 1;
playerNode = GetNode(3, 3);
PlacePlayerObject(playerObj, playerNode.worldPosition);
playerObj.transform.localScale = Vector3.one * 1.2f;
tailParent = new GameObject("tailParent");
}
void CreateApple()
{
appleObj = new GameObject("Apple");
SpriteRenderer appleRenderer = appleObj.AddComponent<SpriteRenderer>();
appleRenderer.sprite = CreateSprite(appleColor);
appleRenderer.sortingOrder = 1;
RandomlyPlacedApple();
}
#endregion
#region Update
void MoveTail()
{
Node prevNode = null;
for (int i = 0; i < tail.Count; i++)
{
SpecialNode p = tail[i];
availbleNodes.Add(p.node);
if (i == 0)
{
prevNode = p.node;
p.node = playerNode;
}
else
{
Node prev = p.node;
p.node = prevNode;
prevNode = prev;
}
availbleNodes.Remove(p.node);
PlacePlayerObject(p.obj, p.node.worldPosition);
}
}
#endregion
#region Utilities
bool isTailNode(Node n)
{
for (int i = 0; i < tail.Count; i++)
{
if(tail[i].node == n)
{
return true;
}
}
return false;
}
void PlacePlayerObject(GameObject obj, Vector3 pos)
{
pos += Vector3.one * .5f;
obj.transform.position = pos;
}
void RandomlyPlacedApple()
{
int ran = Random.Range(0, availbleNodes.Count);
Node n = availbleNodes[ran];
PlacePlayerObject(appleObj, n.worldPosition);
appleNode = n;
}
Node GetNode(int x, int y)
{
if (x < 0 || x > MaxWidth - 1 || y < 0 || y > MaxHeight - 1)
return null;
return grid[x, y];
}
SpecialNode CreateTailNode(int x, int y)
{
SpecialNode s = new SpecialNode();
s.node = GetNode(x, y);
s.obj = new GameObject();
s.obj.transform.parent = tailParent.transform;
s.obj.transform.position = s.node.worldPosition;
s.obj.transform.localScale = Vector3.one * .95f;
SpriteRenderer r = s.obj.AddComponent<SpriteRenderer>();
r.sprite = (playerSprite);
r.sortingOrder = 1;
return s;
}
#endregion
}`
This is a simplified version of the script.
Thanks
Since you have an isTailNode function you should be able to continuously generate new apple nodes and check if they are tail nodes. If they are you generate new ones. This can be achieved using a while loop.
void RandomlyPlacedApple()
{
int ran = Random.Range(0, availbleNodes.Count);
while(isTailNode(availbleNodes[ran]))
{
ran = Random.Range(0, availbleNodes.Count);
}
PlacePlayerObject(appleObj, availbleNodes[ran].worldPosition);
appleNode = availbleNodes[ran];
}
Check if it's inside ez
if(apple.location == snake.location){ changeAppleLocation() }
thanks for reading.
I'm currently building a small memory card game in Unity using C#. I have the main portion of code finished but when I press the play button on a certain scene Unity freezes.
I believe it is due to an infinite While loop, but I can not find the issue. I would really appreciate any help anyone can offer. I will leave my code below. Thanks in advance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine;
public class Pairs : MonoBehaviour {
public Sprite[] face; //array of card faces
public Sprite back;
public GameObject[] deck; //array of deck
public Text pairsCount;
private bool deckSetUp = false;
private int pairsLeft = 13;
// Update is called once per frame
void Update () {
if (!deckSetUp)
{
SetUpDeck();
}
if (Input.GetMouseButtonUp(0)) //detects left click
{
CheckDeck();
}
}//Update
void SetUpDeck()
{
for (int ix = 0; ix < 2; ix++)
{
for(int i = 1; i < 14; i++)//sets up card value (2-10 JQKA)
{
bool test = false;
int val = 0;
while (!test)
{
val = Random.Range(0, deck.Length);
test = !(deck[val].GetComponent<Card>().SetUp);
}//while
//sets up cards
deck[val].GetComponent<Card>().Number = i;
deck[val].GetComponent<Card>().SetUp = true;
}//nested for
}//for
foreach (GameObject crd in deck)
{
crd.GetComponent<Card>().setUpArt();
}
if (!deckSetUp)
{
deckSetUp = true;
}
}//SetUpDeck
public Sprite getBack()
{
return back;
}//getBack
public Sprite getFace(int i)
{
return face[i - 1];
}//getFace
void CheckDeck()
{
List < int > crd = new List<int>();
for(int i = 0; i < deck.Length; i++)
{
if(deck[i].GetComponent<Card>().State == 1)
{
crd.Add(i);
}
}
if(crd.Count == 2)
{
CompareCards(crd);
}
}//CheckDeck
void CompareCards(List<int> crd)
{
Card.NO_TURN = true; //stops cards turning
int x = 0;
if(deck[crd[0]].GetComponent<Card>().Number ==
deck[crd[1]].GetComponent<Card>().Number)
{
x = 2;
pairsLeft--;
pairsCount.text = "PAIRS REMAINING: " + pairsLeft;
if(pairsLeft == 0) // goes to home screen when game has been won
{
SceneManager.LoadScene("Home");
}
}
for(int j = 0; j < crd.Count; j++)
{
deck[crd[j]].GetComponent<Card>().State = x;
deck[crd[j]].GetComponent<Card>().PairCheck();
}
}//CompareCards
}
I believe the issue lies in the while(!test) but i do not know why test never become true.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class Card : MonoBehaviour {
public static bool NO_TURN = false;
[SerializeField]
private int cardState; //state of card
[SerializeField]
private int cardNumber; //Card value (1-13)
[SerializeField]
private bool _setUp = false;
private Sprite back; //card back (Green square)
private Sprite face; //card face (1-10 JQKA)
private GameObject pairsManager;
void Begin()
{
cardState = 1; //cards face down
pairsManager = GameObject.FindGameObjectWithTag("PairsManager");
}
public void setUpArt()
{
back = pairsManager.GetComponent<Pairs>().getBack();
face = pairsManager.GetComponent<Pairs>().getFace(cardNumber);
turnCard();//turns the card
}
public void turnCard() //handles turning of card
{
if (cardState == 0)
{
cardState = 1;
}
else if(cardState == 1)
{
cardState = 0;
}
if (cardState == 0 && !NO_TURN)
{
GetComponent<Image>().sprite = back; // shows card back
}
else if (cardState == 1 && !NO_TURN)
{
GetComponent<Image>().sprite = face; // shows card front
}
}
//setters and getters
public int Number
{
get {return cardNumber;}
set { cardNumber = value;}
}
public int State
{
get { return cardState; }
set { cardState = value; }
}
public bool SetUp
{
get { return _setUp; }
set { _setUp = value; }
}
public void PairCheck()
{
StartCoroutine(pause ());
}
IEnumerator pause()
{
yield return new WaitForSeconds(1);
if (cardState == 0)
{
GetComponent<Image>().sprite = back;
}
else if (cardState == 1)
{
GetComponent<Image>().sprite = face;
}
}
}
Thank you for reading, I will post a link to the github repository if that helps.
github repository
Your deck array has at least one card in it that has _setUp set to true which would make it go in a infinite loop.
The reason it goes in a infinite loop is because it will have set all available _setUp to true and it would keep looking for _setUp that are set to false and it will never find any.
The reason you need at least 26 object that have _setUp to false is because in the nested for loop you loop 13 times and then you do that twice which gives a total of 26 loops. So you need at least 26 objects.
What you can do to make sure that they're all false is to set them all to false before entering the for loop
for(int i = 0; i < deck.Length; i++)
{
deck[i].GetComponent<Card>().SetUp = false;
}
So I'm playing around with the BouncyGame. I made it so that when you start the game you need to press the screen for it to start. I would like to implement this whenever you play a new round as well. I tried to reuse this att the bottom of my code but it made it extremely laggy.
// Register for touch events
var touchListener = new CCEventListenerTouchAllAtOnce();
touchListener.OnTouchesEnded = OnTouchesEnded;
touchListener.OnTouchesMoved = OnTouchesEnded;
AddEventListener(touchListener, this);
}
void OnTouchesEnded(List<CCTouch> touches, CCEvent touchEvent)
{
if (touches.Count > 0)
{
Schedule(RunGameLogic);
scoreLabel.Text = "Score: 0";
paddleSprite.RunAction(new CCMoveTo(.1f, new CCPoint(touches[0].Location.X, paddleSprite.PositionY)));
}
}
I have no idea how to do this, tried for 2 hours with 0 results. Any suggestions are welcome.
Here's the full code.
using System;
using System.Collections.Generic;
using CocosSharp;
using Microsoft.Xna.Framework;
namespace CocosSharpGameTest
{
public class IntroLayer : CCLayerColor
{
// Define a label variable
CCLabel scoreLabel;
CCSprite paddleSprite, ballSprite;
public IntroLayer() : base(CCColor4B.Black)
{
// create and initialize a Label
scoreLabel = new CCLabel("Tap to GO!", "Arial", 80, CCLabelFormat.SystemFont);
// add the label as a child to this Layer
scoreLabel.PositionX = 50;
scoreLabel.PositionY = 1000;
scoreLabel.AnchorPoint = CCPoint.AnchorUpperLeft;
AddChild(scoreLabel);
paddleSprite = new CCSprite("paddle.png");
AddChild(paddleSprite);
ballSprite = new CCSprite("ball.png");
AddChild(ballSprite);
}
protected override void AddedToScene()
{
base.AddedToScene();
// Use the bounds to layout the positioning of our drawable assets
CCRect bounds = VisibleBoundsWorldspace;
// position the label on the center of the screen
paddleSprite.PositionX = 100;
paddleSprite.PositionY = 100;
ballSprite.PositionX = 320;
ballSprite.PositionY = 640;
// Register for touch events
var touchListener = new CCEventListenerTouchAllAtOnce();
touchListener.OnTouchesEnded = OnTouchesEnded;
touchListener.OnTouchesMoved = OnTouchesEnded;
AddEventListener(touchListener, this);
}
void OnTouchesEnded(List<CCTouch> touches, CCEvent touchEvent)
{
if (touches.Count > 0)
{
Schedule(RunGameLogic);
scoreLabel.Text = "Score: 0";
paddleSprite.RunAction(new CCMoveTo(.1f, new CCPoint(touches[0].Location.X, paddleSprite.PositionY)));
}
}
float ballXVelocity;
float ballYVelocity;
// How much to modify the ball's y velocity per second:
const float gravity = 140;
int score = 0;
void RunGameLogic(float frameTimeInSeconds)
{
// This is a linear approximation, so not 100% accurate
ballYVelocity += frameTimeInSeconds * -gravity;
ballSprite.PositionX += ballXVelocity * frameTimeInSeconds;
ballSprite.PositionY += ballYVelocity * frameTimeInSeconds;
bool overlap = ballSprite.BoundingBoxTransformedToParent.IntersectsRect(paddleSprite.BoundingBoxTransformedToParent);
bool movingDown = ballYVelocity < 0;
if (overlap && movingDown)
{
ballYVelocity *= -1;
const float minXVelocity = -300;
const float maxXVelocity = 300;
ballXVelocity = CCRandom.GetRandomFloat(minXVelocity, maxXVelocity);
score++;
scoreLabel.Text = "Score: " + score;
}
float ballRight = ballSprite.BoundingBoxTransformedToParent.MaxX;
float ballLeft = ballSprite.BoundingBoxTransformedToParent.MinX;
float screenRight = VisibleBoundsWorldspace.MaxX;
float screenLeft = VisibleBoundsWorldspace.MinX;
bool shouldReflectXVelocity =
(ballRight > screenRight && ballXVelocity > 0) ||
(ballLeft < screenLeft && ballXVelocity < 0);
if (shouldReflectXVelocity)
{
ballXVelocity *= -1;
}
if (ballSprite.PositionY < VisibleBoundsWorldspace.MinY)
{
ballSprite.PositionX = 320;
ballSprite.PositionY = 640;
ballXVelocity = 0;
ballYVelocity = 0;
ballYVelocity *= -1;
scoreLabel.Text = "Score: 0";
score = 0;
}
}
}
}
Thanks in advance!
Figured it out!
There is an "Unschedule" Method built into Cocossharp.
Ref. https://developer.xamarin.com/api/namespace/CocosSharp/
I just added
Unschedule(RunGameLogic);
at the very en of my RunGameLogic method under
if (ballSprite.PositionY < VisibleBoundsWorldspace.MinY)
So once the ballSprite is out of bounds it will Unschedule what i Scheduled in my OntouchesEnded method. That means the code goes back to listening for touches.
Might have made some errors, but this is as best I could figure it out and it works!
This question already has answers here:
CS0120: An object reference is required for the nonstatic field, method, or property 'foo'
(9 answers)
Closed 6 years ago.
i have a private method is ball.cs class in witch i want to modify a bool type from margins.cs class, but i get the Error An object reference is required for the non-static field, method, or property 'PingPong.Margins.win'.
Can someone help me with this?
this is the ball class code :
namespace PingPong
{
public class Ball
{
private PictureBox ball;
Random rand= new Random();
Player leftSidePlayer, rightSidePlayer;
int xSpeed, ySpeed;
public Ball(PictureBox aBall, Player leftSidePlayer, Player rightSidePlayer)
{
this.ball = aBall;
this.leftSidePlayer = leftSidePlayer;
this.rightSidePlayer = rightSidePlayer;
xSpeed = 1;
ySpeed = 2;
resetBall();
}
internal void processmove()
{
var bottom = Margins.bottomOfWorld - ball.Height;
DoMove();
if(ball.Location.Y >= bottom || ball.Location.Y <= Margins.topOfWorld)
{
ySpeed *= -1;
}
if (ball.Location.X <= Margins.leftOfWorld)
{
Score(leftSidePlayer);
}
else if (ball.Location.X >= Margins.rightOfWorld - ball.Width)
{
Score(rightSidePlayer);
}
if ((leftSidePlayer.paddle.Bounds.IntersectsWith(ball.Bounds)) || (rightSidePlayer.paddle.Bounds.IntersectsWith(ball.Bounds)))
{
xSpeed *= -1;
if ((ySpeed <= 6 && ySpeed >=-6) && (xSpeed <= 5 && xSpeed >=-5) )
{
if(ySpeed < 0)
{
ySpeed -= 1;
}else
{
ySpeed += 1;
}
if (xSpeed < 0)
{
xSpeed -= 1;
}
else
{
xSpeed += 1;
}
}
}
}
private int DoMove()
{
var bottom = Margins.bottomOfWorld - ball.Height;
ball.Location = new Point(ball.Location.X + xSpeed, Math.Max(Margins.topOfWorld, Math.Min(bottom, ball.Location.Y + ySpeed)));
return bottom;
}
private void Score(Player winningPlayer)
{
winningPlayer.scoreNumber++;
if(winningPlayer.scoreNumber == 7)
{
if(winningPlayer == leftSidePlayer )
{
Margins.win = true;
}else if(winningPlayer == rightSidePlayer)
{
Margins.win = false;
}
}
resetBall();
}
private void resetBall()
{
ball.Location = new Point((Margins.leftOfWorld + Margins.rightOfWorld) / 2, (Margins.bottomOfWorld + Margins.topOfWorld) / 2);
do
{
xSpeed = rand.Next(-3, 3);
ySpeed = rand.Next(-3, 3);
} while(Math.Abs(xSpeed) + Math.Abs(ySpeed) <= 3);
}
}
}
and this is the margins class code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PingPong
{
public class Margins
{ //Marimea ferestrei 1000; 600
public const int topOfWorld = 0, bottomOfWorld = 560, leftOfWorld =0, rightOfWorld = 1000;
public bool? win = null;
}
}
the error i get is in private void score when i use the margins.win = true
and margins.win = false .
You have a class with some static (or constant) variables now. Next to that, there is the instance variable win. That means you have to create an instance of Margins in order to get or set it.
If you really need a static class since there can always be just one Margins instance, make the class and its variables static:
public static class Margins
{ //Marimea ferestrei 1000; 600
public const int topOfWorld = 0, bottomOfWorld = 560, leftOfWorld =0, rightOfWorld = 1000;
public static bool? win = null;
}
If not, make a proper instance of Margins:
private Margins margins = new Margins();
this.margins.win = true;
I am trying to make a slider puzzle game and I keep getting the error "NullReferenceException was unhandled" when I call myBoard.paint(e.Graphics) in my form1. Please help me!!!
Here is my code for Form1 (Let me know if I need to post some of my other classes code):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace SliderPuzzle
{
public partial class Form1 : Form
{
private int tileSize;
private int rowsCols;
private SlidePuzzle myBoard;
private Stopwatch timer;
private int moveCount;
public Form1()
{
InitializeComponent();
pictureBox1.TabIndex = 3;
pictureBox1.Size = new Size(100, 50);
pictureBox1.Location = new Point(16, 71);
pictureBox1.BackColor = Color.PaleGreen;
pictureBox1.BorderStyle = BorderStyle.Fixed3D;
pictureBox1.TabStop = false;
tileSize = imageList1.ImageSize.Width;
rowsCols = 3;
pictureBox1.Width = rowsCols * tileSize;
pictureBox1.Height = rowsCols * tileSize;
}
public void initGame()
{
myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);
timer = new Stopwatch();
moveCount = 0;
timer.Start();
}
private void Form1_Load(object sender, EventArgs e)
{
initGame();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
this.myBoard.paint(e.Graphics);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (myBoard.move(e.Y / tileSize, e.X / tileSize))
++moveCount;
Refresh();
if (!myBoard.winner())
return;
timer.Stop();
if (MessageBox.Show(string.Format("You won!!\nIt took you {0} moves and {1:F2} seconds.\nPlay again?", (object)moveCount, (object)timer.Elapsed.TotalSeconds), "Game Over", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.No)
{
Close();
}
else
{
initGame();
Refresh();
}
}
}
}
Update #1: Okay, so I moved myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1); to my constructor, but now none of the images are showing up on it. Here is what It looks like vs what it is supposed to look like:
Edit #2: Okay, I moved it back to where it was before and put
if (this.myBoard != null)
this.myBoard.paint(e.Graphics);
instead, and it works a little better and looks better as well. But the images not showing up is still a problem.
Edit #3: Here is the SliderPuzzle.Paint Code:
public void paint(Graphics g)
{
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
this.myGrid.get(new Location(r, c)).paint(g);
}
}
Edit #4: Here is the code for the SliderPuzzle Class:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace SliderPuzzle
{
internal class SlidePuzzle
{
private static Random rand = new Random();
private int myTileSize;
private BoundedGrid myGrid;
private ImageList myImages;
private Location myBlankLoc;
static SlidePuzzle()
{
}
public SlidePuzzle(int rowsCols, int tileSize, ImageList images)
{
this.myTileSize = tileSize;
this.myGrid = new BoundedGrid(rowsCols, rowsCols);
this.myImages = images;
this.myBlankLoc = new Location(rowsCols - 1, rowsCols - 1);
this.initBoard();
}
private void initBoard()
{
int index1 = 0;
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
{
this.myGrid.put(new Location(r, c), new Tile(index1, this.myTileSize, new Location(r, c), this.myImages.Images[index1]));
++index1;
}
}
for (int index2 = 0; index2 < 1000; ++index2)
{
Location adjacentLocation = this.myBlankLoc.getAdjacentLocation(SlidePuzzle.rand.Next(4) * 90);
if (this.myGrid.isValid(adjacentLocation))
{
this.swap(this.myBlankLoc, adjacentLocation);
this.myBlankLoc = adjacentLocation;
}
}
}
public bool move(int row, int col)
{
Location loc1 = new Location(row, col);
if (Math.Abs(this.myBlankLoc.getRow() - row) + Math.Abs(this.myBlankLoc.getCol() - col) != 1)
return false;
this.swap(loc1, this.myBlankLoc);
this.myBlankLoc = loc1;
return true;
}
public bool winner()
{
int num = 0;
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
{
if (this.myGrid.get(new Location(r, c)).getValue() != num)
return false;
++num;
}
}
return true;
}
private void swap(Location loc1, Location loc2)
{
Tile tile1 = this.myGrid.put(loc2, this.myGrid.get(loc1));
Tile tile2 = this.myGrid.put(loc1, tile1);
tile1.setLocation(loc1);
tile2.setLocation(loc2);
}
public void paint(Graphics g)
{
for (int r = 0; r < this.myGrid.getNumRows(); ++r)
{
for (int c = 0; c < this.myGrid.getNumCols(); ++c)
this.myGrid.get(new Location(r, c)).paint(g);
}
}
}
}
Update #5: Here is the Tile Class:
using System.Drawing;
namespace SliderPuzzle
{
internal class Tile
{
private int myValue;
private int mySize;
private Location myLoc;
private Image myImage;
public Tile(int value, int tileSize, Location loc, Image img)
{
this.myValue = value;
this.mySize = tileSize;
this.myLoc = loc;
this.myImage = img;
}
public int getValue()
{
return this.myValue;
}
public void setLocation(Location newLoc)
{
this.myLoc = newLoc;
}
public void paint(Graphics g)
{
g.DrawImage(this.myImage, this.myLoc.getCol() * this.mySize, this.myLoc.getRow() * this.mySize);
}
}
}
Edit #6: Here is the Location Class:
namespace SliderPuzzle
{
internal class Location
{
public const int LEFT = -90;
public const int RIGHT = 90;
public const int HALF_LEFT = -45;
public const int HALF_RIGHT = 45;
public const int FULL_CIRCLE = 360;
public const int HALF_CIRCLE = 180;
public const int AHEAD = 0;
public const int NORTH = 0;
public const int NORTHEAST = 45;
public const int EAST = 90;
public const int SOUTHEAST = 135;
public const int SOUTH = 180;
public const int SOUTHWEST = 225;
public const int WEST = 270;
public const int NORTHWEST = 315;
private int row;
private int col;
public Location(int r, int c)
{
this.row = r;
this.col = c;
}
public int getRow()
{
return this.row;
}
public int getCol()
{
return this.col;
}
public Location getAdjacentLocation(int direction)
{
int num1 = (direction + 22) % 360;
if (num1 < 0)
num1 += 360;
int num2 = num1 / 45 * 45;
int num3 = 0;
int num4 = 0;
if (num2 == 90)
num3 = 1;
else if (num2 == 135)
{
num3 = 1;
num4 = 1;
}
else if (num2 == 180)
num4 = 1;
else if (num2 == 225)
{
num3 = -1;
num4 = 1;
}
else if (num2 == 270)
num3 = -1;
else if (num2 == 315)
{
num3 = -1;
num4 = -1;
}
else if (num2 == 0)
num4 = -1;
else if (num2 == 45)
{
num3 = 1;
num4 = -1;
}
return new Location(this.getRow() + num4, this.getCol() + num3);
}
public bool equals(Location other)
{
if (this.getRow() == other.getRow())
return this.getCol() == other.getCol();
else
return false;
}
public int hashCode()
{
return this.getRow() * 3737 + this.getCol();
}
public int compareTo(Location otherLoc)
{
if (this.getRow() < otherLoc.getRow())
return -1;
if (this.getRow() > otherLoc.getRow())
return 1;
if (this.getCol() < otherLoc.getCol())
return -1;
return this.getCol() > otherLoc.getCol() ? 1 : 0;
}
public string toString()
{
return "(" + (object)this.getRow() + ", " + (string)(object)this.getCol() + ")";
}
}
}
Edit #7: Here is the last class, the BoundedGrid Class:
using System;
using System.Collections.Generic;
namespace SliderPuzzle
{
internal class BoundedGrid
{
private Tile[,] occupantArray;
public BoundedGrid(int rows, int cols)
{
this.occupantArray = new Tile[rows, cols];
}
public int getNumRows()
{
return this.occupantArray.GetLength(0);
}
public int getNumCols()
{
return this.occupantArray.GetLength(1);
}
public bool isValid(Location loc)
{
if (0 <= loc.getRow() && loc.getRow() < this.getNumRows() && 0 <= loc.getCol())
return loc.getCol() < this.getNumCols();
else
return false;
}
public List<Location> getOccupiedLocations()
{
List<Location> list = new List<Location>();
for (int r = 0; r < this.getNumRows(); ++r)
{
for (int c = 0; c < this.getNumCols(); ++c)
{
Location loc = new Location(r, c);
if (this.get(loc) != null)
list.Add(loc);
}
}
return list;
}
public Tile get(Location loc)
{
if (!this.isValid(loc))
throw new Exception("Location " + (object)loc + " is not valid");
else
return this.occupantArray[loc.getRow(), loc.getCol()];
}
public Tile put(Location loc, Tile obj)
{
if (!this.isValid(loc))
throw new Exception("Location " + (object)loc + " is not valid");
if (obj == null)
throw new NullReferenceException("obj == null");
Tile tile = this.get(loc);
this.occupantArray[loc.getRow(), loc.getCol()] = obj;
return tile;
}
public Tile remove(Location loc)
{
if (!this.isValid(loc))
throw new Exception("Location " + (object)loc + " is not valid");
Tile tile = this.get(loc);
this.occupantArray[loc.getRow(), loc.getCol()] = (Tile)null;
return tile;
}
}
}
Edit #8: When I click on the picturebox, the program crashes and it says the the timer.Stop(); in form1 gives me a NullReferenceException!!!
Edit #9: Okay, THAT worked... I have found that the images still do not show up, but I think that they are never being placed on the grid. When I click on the grid ( still has no images) It says that I have won. This should only display after I move the tiles into the correct order. Any idea what is going on?
Edit #10: My program finally works now! It turns out I had something missplaced in the constructor of form1, now everything works! The images show up and everything! How cool is that!!!
THANK YOU EVERYONE FOR YOU CONTRIBUTIONS, I WILL NOW GET A GREAT GRADE ON MY SCHOOL PROJECT!
This is one of those problems that will give you headaches. Trying to nail down the exact sequence of events is fine, when you can guarantee that the events are never going to be invoked out of sequence. Unfortunately the Paint event is one that gets fired at all sorts of odd times, any may be fired even before the Load event.
The only real answer is to never rely on events being fired in a particular sequence. Always check that your myBoard object is valid before trying to paint it:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (this.myBoard != null)
this.myBoard.paint(e.Graphics);
}
And yes, as others have noted, it is better to create all of your objects as soon as possible - in the constructor for instance, which is what a constructor is for after all. Even then, you can still get events being fired by actions in the constructor that will ruin your whole day. Your even handlers should be set as late as possible in the constructor code to account for this.
Rule(s) of thumb:
if you're creating custom objects that will be used in event handlers, always try to create and initialize them prior to calling InitializeComponent in the constructor.
Wire up your event handlers as late as possible, especially for things like Paint - it won't be useful before your constructor is done, so do it last in the constructor.
Your actual Form1_Load event never gets called (the one where you are initializing your board).
Add this code at the end of the Form1 constructor this.Load +=Form1_Load;
Your pictureBox1.Paint event is raised before your Form1.Load event. Move
myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);
to your constructor and you should be fine.