#chess
1 messages · Page 1 of 1 (latest)
good idea
So, every piece can move, right
therefore, every piece should be able to tell you a list of squares it's allowed to move to
right, which was the intention of this function
you don't care if it's a Rook or a Pawn
you just want to know where the piece can move
obviously ive only done rook movement logic so far
as-is, you'll wind up having to do something like this
if (piece is Rook)
(piece as Rook).GetMoves();
else if (piece is Pawn)
(piece as Pawn).GetMoves();
etc.
so, here's what you should do:
public abstract class Piece {
public Vector2Int position; //Position On Board XY 1-8
public Type type; // Piece Type EG: Rook, King ETC
public Colour colour; // White/Black
protected Board board;
public void SetBoard(Board board) { this.board = board; }
public abstract List<Vector2Int> LegalMoves();
}
that last method is abstract
therefore, anything that inherits from Piece must provide a definition for it
e.g.
public class Rook {
public override List<Vector2Int> LegalMoves() {
return new List<Vector2Int>();
}
}
obviously that's not a correct implementation
but you'd write something appropriate there
now, I can do this:
Piece piece = /* idk */
foreach (var move in piece.LegalMoves()) {
// do something to highlight the square
}
no matter what the piece is, we can just ask it for where it can move
we could go a step further, too -- consider how it's always illegal to move in a way that checks your king
Right, so the public abstract List<Vector2Int> LegalMoves(); would that constantly update if legal moves change
so every piece should check for this when considering if a move is legal
so, I would actually just make a virtual method that decides if a single move is legal
public virtual bool LegalMove(Vector2Int pos) {
/* code here */
}
so a seperate method for the move itself that the person is attempting to do, right
a virtual method is one that can be overridden by a child class
but the child can still call it
so, Piece's LegalMove method would check if the player's king is now in check after making that move
then, in Rook, you'd do something like
Right I'm following, is it alright first if i sort out the original piece situation real quick?
public override void LegalMove(Vector2Int pos) {
if (!base.LegalMove(pos))
return false;
// check if you can move there
}
oh yeah, go ahead
checking legal moves is actually gonna be a little tricky
e.g. what if I capture a piece that would otherwise be threatening my king if I just moved out of the way?
you'd want a way to make a copy of the board state, make the move, and then check if the king is in danger
things can get tricky quickly :p
Yeah, I think i can do it though. The language i know best is JavaScript and as im sure you can tell im a beginner at C# so a lot of these new things in it take a bit to wrap my head around
I'd have to sit down and think about that for a little while
Of course, you could always start with just "where can this piece go?", even if it would be an illegal board state
and then, later on, add that logic to Piece's LegalMove function
Yeah that's probably best.
So the rook script
does the class need to derive from piece still? because it also needs to from MonoBehaviour to attatch to a prefab
specifically for this reason
but i also need to attach the rook script
this will make it harder to make a copy of the current board state so that you can check if a move is legal
Yeah true
well, you would be attaching just the Rook script!
it's a Piece, which is a MonoBehaviour
it says this weirdly though. public class Rook : Piece even though i have this
cs public abstract class Piece : MonoBehaviour and piece is here
make sure it refreshed the files and doesn't have any compile errors
so there's another way you can think about this whole thing:
- the board state is just made of plain old C# classes
- you transform that into physical pieces on the board whenever you want to update the visuals
so the pieces on the board are just a snapshot of what the board state is
hmm. That could be a possibility. So i'd only be touching the board layout and then just listening for changes to update the board visually
Maybe it'd be easier
Yeah.
I'd need to think about it some more :p
I definitely think that you should have an abstract Piece class that each specific kind of piece inherits from
but then the board would just be a bunch of dumb objects
when you select one, it asks the corresponding piece where it can move
I think I kind of prefer this alternate method you gave
not sure
yeah, it feels better for a game where you need to look at future board states
(especially if you try making an AI for it :p )
I haven't implemented a board game before, so it makes sense I wouldn't think of that at first
yeah i did think one day of trying some machine learning against myself and seeing if i can train an ai from scratch to beat me lol. I mean i'll likely make it multiplayer first
and, of course, another thing: the simplest way to store the board state would be...an array of structs storing color and piece type
in that case, you wouldn't have a list of Rooks and Pawns and Bishops
it'd just be dumb data
and you'd have a function that knows what to do for every piece type
...which would completely get rid of the abstract Piece class
the convenient part here: it'd be super easy to make a copy of it
you wouldn't be able to just say pieces[new(3,4)].GetLegalMoves()
you mean that i would be able?
man, I might be less confident about what to do than when I started talking about this, haha
Game Design (tm)
lol
I think i'm going to start again with a new project
and port code over
It'll probably be easier
I'd definitely look up some tutorials. Even if you don't follow them, you can see how they think that data should be structured
there are several different ways to do this and I can see headaches with each one
Yeah that's what I did initially, but they tended to trail down a path which wasn't very expandable for implementations like multiplayer and such
I've been occasionally referring to some just to see what they do for certain functionality
so, basically, you could do this two ways
- the object-oriented programming way
each piece is a class, and each class has a function that tells you where that piece can legally move
- the procedural way
each piece is a bit of data, and there's a function that can tell you where that piece can move
"OOP" is just when you glue the functions onto the data
it's really nice for modeling many kinds of problems
you don't have a list of pieces and a big function that knows how to handle all of the pieces
each piece just knows what it can do
Both have their benefits and drawbacks so I honestly can't decide. I'm leaning towards the new method as the board as a whole would just be easier to see and make the more complicated logic behind legal moves to do with checking the king and such easier
also, one benefit of having the pieces on the board separate from the actual logic
you can change how the game is visualized very easily
Which would probably make netcode easier
right
you'd just synchronize the board state
no need to like
network the transforms of every single piece
Maybe this is the way to go then.
yeah, 100% if you want to make this networked later
in that case, you want to have a nice, small, tidy state
and derive as much as you can from that state
Right, I've already looked at some courses on multiplayer using unity's fairly new system and it does look like a hassle, so best to make it as easy as possible
I genuinely have no idea what's going on here
using System;
using UnityEngine;
[CreateAssetMenu(fileName = "New Board Layout", menuName = "ScriptableObjects/Board Layout")]
public class BoardLayout : ScriptableObject
{
public Piece[] pieces;
public const int boardSize = 8;
nothing at all here, ive refreshed unity and even restarted
Piece probably isn't serializable
im stupid, I forgot that was a thing, thank you. I was only looking at the script itself not thinking about whether the class itself was
public enum Type
{
King,
Queen,
Rook,
Bishop,
Knight,
Pawn
}
public enum PieceColour
{
White,
Black
}
[Serializable]
public abstract class Piece : MonoBehaviour
{
public PieceColour colour;
public Type type;
public Vector2Int position;
public abstract List<Vector2Int> LegalMoves();
}
using System.Collections.Generic;
using UnityEngine;
public class Rook : Piece
{
public Vector2Int[] moveDirections = new Vector2Int[]
{Vector2Int.left, Vector2Int.up, Vector2Int.right, Vector2Int.down };
public override List<Vector2Int> LegalMoves()
{
List<Vector2Int> legalMoves = new List<Vector2Int>();
foreach (Vector2Int direction in moveDirections)
{
for (int i = 1; i <= BoardLayout.boardSize; i++)
{
Vector2Int coord = ?
}
}
return legalMoves;
}
}```
So following what you said i have this again, but i think im still a bit unclear on how these overrides work so im not sure as to how i would pass the piece's position into this function
If you go with this design -- where there's a Piece class that holds information about the piece -- then you can just access position!
you don't have to pass the position in; the Piece knows its own position
how do i go about accessing it though? Piece.position won't work as i expect because it isnt static
is there anything im doing wrong with the override function
ah, that makes sense
So where exactly should the script go if the prefabs are just for visuals? Thats one thing im unsure on
if we go with the "pieces are just visuals" design (which I do think is the right way to go, after talking through it), then Piece wouldn't be a behaviour
Will the rook script still be then?
I would expect that Rook/Bishop/Pawn/etc. would be derivatives of Piece
so they would also not be behaviours
Gotcha
So, i've done the basic scripting for calculating legal moves of all pieces (ignoring checks for now). But im not sure as to how im supposed to fill the scriptable object with pieces if it piece doesnt inherit from monobehaviour, should i create a seperate struct just with piece prefabs, type, spawn position etc?
I'd make a list of structs that store the type, position, and color
you'd look up the prefab based on the type, like you were doing originally
Yeah that's what i was thinking, ty
does that not mean im still going to end up with what i had before tho? because then im going to have this PieceLayout struct in addition to the piece struct on the scripts of the pieces
[Serializable]
public abstract class Piece
{
public PieceColour colour;
public Type type;
public Vector2Int position;
protected BoardLayout boardLayout;
public void SetBoard(BoardLayout board)
{
boardLayout = board;
}
public abstract List<Vector2Int> LegalMoves();
}
[Serializable]
public struct PieceLayout
{
public PieceColour colour;
public Type type;
public Vector2Int position;
}```
You will, yeah -- and I guess you could re-use the Piece class if you really wanted to.
One thing you wouldn't want to do is directly use the list of pieces from the BoardLayout without making a copy of them first. You'd be directly manipulating the ScriptableObject's contents, so you'd wind up changing the asset on disk!
So i need a separate one for only start layout and a new one entirely for the current layout
Right, since those are two separate concepts here (the Piece has some extra information, like a reference to the Board)
if you moved all of that out of the piece (maybe the Board itself decides where pieces can move), then I bet you could make the start config use the exact same type
Alright, ill give it a go
Any idea why this has turned into this?
public class Piece : MonoBehaviour
{
public PieceColour colour;
public Type type;
public Vector2Int position;
protected BoardLayout boardLayout;
public void SetBoard(BoardLayout board)
{
boardLayout = board;
}
}```
[SerializeField] private BoardLayout initialLayout; // Do not modify | Will permanently write to disk!```
also im not sure if this is the right way to do this because i've never used these before, but ive added this interface
public interface IDefinePiece
{
public abstract void DefinePieceValues(Piece piece);
}```
and then referenced it in each script
IDefinePiece script = instantiatedObject.GetComponent<IDefinePiece>();
script.DefinePieceValues(piece);``` in order to give each piece its properties here
if Piece is a monobehaviour, then it'll expect you to assign references to those fields
ah i've got this damn issue again
now i cant put the scripts on the prefabs
ahhhh
god ive got myself in a loop
I dont know how to get out of
you need to decide if Piece is going to be a monobehaviour attached to things in the scene
or just data
I think your original plan was the latter
Yeah, but im not sure how to use that data if its not attatched to the pieces in play
I would have the Piece class refer to a specific object in the scene
maybe you can call that component PhysicalPiece
or something
As in like add a game object reference?
Should I make piece a scriptable object in that case
I'm not sure there! I've only ever used SOs for readonly data
Doesn’t a script have to be attached to an object to run if not though?
And obviously I can’t do that because it doesn’t inherit monobehaviour
well, it's not like pieces need to do something every frame, right
you'd just run functions on the Piece objects as needed
So I’d just have all the scripts attached to my game controller and run functions on them as needed then?