2022. 7. 25. 04:18ㆍ유니티 unity
이번에는 socket.io와 mysql를 이용해서 오목을 만들겠습니다.
mysql는 아이디 비밀번호 승리 패배 로그인 기록을 저장을 하겠습니다.
https://dev.mysql.com/downloads/mysql/
들어가셔서 설치해주세요
설치 진행해주세요!
비밀번호 설정할 때 기억해주시고
아이디 비밀번호 닉네임 승리 패배 생성시간 로그인 시간
이렇게 저장하기 위해서 만듭니다.
pk -> Primary key , 기본키 절대로 중복되어 나타날 수 없는 단일 값을 가집니다. null 못 들어옵니다.
nn-> Not Null, 빈 값 못 들어옴
UQ -Unique, 중복 값 x
b - Binary 데이터를 이진 문자열로 저장
UN- Unsigned, -범위 삭제
ZF- Zero Filled, 컬럼 크기보다 작은 값을 넣었을 경우 0으로 채운 뒤 삽입시킴
AI - Auto Increment , Insert 할 때마다 값 1씩 늘어남
G - Generated , 다른 열을 기반으로 한 수식으로 생성된 값
Defaulf/Expression - 기본값
code 돌아가서 npm i mysql 치시면
mysql 설치는 끝났습니다.
2번 페이지에서 만들었던 채팅 프로그램에다가 추가하는 식으로 하겠습니다.
mysql 추가하는 방법
var mysql = require('mysql');
const dbconfig = require('./config/config.js');
const sql = mysql.createConnection(dbconfig);
user랑 password는 아까 설치 시 넣었던 아이디 비번 넣어주시고
database는 아까 만들었던 데이터베이스 넣어주세요
회원 가입할 때 추가하고
로그인할 때는 찾고
승리와 패배를 저장하기만 하면 되니까
테이블에 추가는 INSERT INTO
찾을 때는 SELECT
저장은 UPDATE
사용하면 됩니다.
비번 저장은 bcypt 모듈을 사용했습니다.
npm i bcrypt로 설치하고
const bcrypt = require('bcrypt')
const 변수= bcrypt.hashSync(비밀번호, 10)
뒤에 숫자는 salt인데 암호화를 숫자만큼 돌립니다 높으면 높을수록 속도가 느려집니다.
비밀번호 비교는
bcrypt.compareSync(비밀번호, 암호화된 비밀번호)로 하시면 됩니다 true false로 나와요
UI는 로그인 창과 회원가입 창을 만들었습니다.
회원가입은 INSERT INTO를 이용해서 만듭니다.
우선 회원 가입하기 전에 해당 아이디가 있는지 파악한 뒤 처리합니다.
server.js
socket.on('CreateCheck', (id, password) => {
//회원가입 체크
sql.query('SELECT * FROM users WHERE ID=?', [id], function (error, results) {
//회원가입 하기전에 아이디 중복확인을 합니다.
if (error) throw error;
if (results.length > 0) {
//아이디가 이미 있다면 실패
console.log("회원가입 실패")
socket.emit('CreateFailed')
}
else {
sql.query('INSERT INTO users (ID, PassWord, createTime) VALUES(?,?,?)', [id, bcrypt.hashSync(password, 10), new Date()], function (error, results) {
if (error) {
console.log(error)
}
else {
console.log(id + ": 회원가입 성공")
socket.emit('Create')
}
})
}
})
})
로그인
server.js
socket.on('LoginCheck', (id, password) => {
sql.query('SELECT * FROM users WHERE ID=?', [id], function (error, results) {
if (error) throw error;
if (results.length > 0) {
if (bcrypt.compareSync(password, results[0].PassWord)) {
//암호화된 비밀번호를 비교합니다.
var check = true;
for (var k in Users) {
if (Users[k].loginID == id)
check = false;
break;
}
//현재 접속된 아이디가 있는지 파악합니다.
if (check) {
console.log(id + ": 로그인 성공")
Users[socket.id].loginID = id;
Users[socket.id].nickname = results[0].nickName;
Users[socket.id].victory = results[0].victory
Users[socket.id].defeat = results[0].defeat
socket.emit('Login', results[0].nickName, results[0].victory, results[0].defeat)
sql.query('UPDATE users SET loginlasttime=? WHERE ID=? ', [new Date(), id], function (error, results) {
{
if (error)
console.log(error)
}
})
}
else {
//동일한 아이디가 들어왔으니 오류
console.log("중복 불가!")
socket.emit('LoginFailed2')
}
}
else {
//비밀번호가 틀리면 오류
console.log("비밀번호 틀림")
socket.emit('LoginFailed')
}
} else {
//해당 아이디가 없으면 오류
console.log("로그인 실패")
socket.emit('LoginFailed')
}
})
})
로그인이 끝나면 닉네임 확인을 하고 없다면
닉네임 설정 UI가 뜹니다.
닉네임 설정은 2 페이지 꺼 그대로 썼습니다.
server.js
socket.on('NickNameCheck', (nickName, id) => {
sql.query('SELECT * FROM users WHERE nickName=?', [nickName], function (error, results) {
//닉네임을 데이터베이스에서 찾습니다.
if (error) throw error;
if (results.length > 0) {
//해당 닉네임이있다면 오류
socket.emit('NickNameFailed')
console.log("닉네임 겹침")
} else {
sql.query('UPDATE users SET nickName=? WHERE ID=? ', [nickName, id], function (error, results) {
//데이터베이스에 수정합니다.
{
if (error) {
console.log(error)
}
else {
Users[socket.id].nickname = nickName;
console.log(nickName + ": 닉네임 설정 성공")
socket.emit('NickName')
}
}
})
}
})
})
방생성 , 방참가는 그대로고
방참여시 ui를 수정했습니다.
플레이어 목록과 채팅 목록을 왼쪽 토글로 설정해서 나눴습니다.
왼쪽 위 버튼을 클릭해야지 참여를 할 수 있게 설정했습니다.
왼쪽 버튼 클릭하고 1번이라면 게임 시작 버튼이 생깁니다.
1번 플레이어 2번 플레이어가 모두 있어야지 게임이 시작되게 했습니다.
게임 시작하면 반반 확률로 1번이 흑 백을 설정합니다.
바둑판 줄에 마우스를 올리면 표시가 나오는데
GridLayout Group밑에다가 미리 돌을 깔아놓는 방법을 선택했습니다.
돌들을 알파 값을 조절해서 안 보이게 했고 클릭 시 보이게 했습니다.
돌 위에 마우스가 들어오면 자식들이 활성화되게 했습니다.
StroneHelp는 흰색 네모고 No는 X가 표시 오브젝트입니다.
omokClick.cs (돌이 가지고 있는 스크립트)
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class omokClick : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
[Header("세로")] public int column; //세로 ↕
[Header("가로")] public int row; //가로 ↔
[Space(20)] [SerializeField] private Image icon; //본인 Image
[SerializeField] private GameObject stoneHelopOb; //네모 흰색
[SerializeField] private GameObject noOb; // x 표시
public bool Check()
//선택을 할 수 있는지 확인하는 스크립트
{
if (omokManager.inst.IsPlay == false)
//플레이중이 아님
{
return false;
}
if (omokManager.inst.IsBlackTurn)
//블랙턴
{
if (omokManager.inst.myPlayType != ePlayType.BLACK)
//본인의 돌 타입이 블랙이 아니라면 false
{
return false;
}
}
else
{
if (omokManager.inst.myPlayType != ePlayType.WHITE)
//블랙턴이 아니고 본인의 돌 타입이 흰돌이 아니면 false
{
return false;
}
}
if (omokManager.inst.ball[column, row] != 0)
//해당 돌에 돌이 있다면 false
{
return false;
}
return true;
}
public void OnPointerEnter(PointerEventData eventData)
//마우스가 들어오면
{
if (Check() == false)
{
return;
}
stoneHelopOb.gameObject.SetActive(true);
//네모흰색이 표시되도록 합니다.
}
public void OnPointerExit(PointerEventData eventData)
//마우스가 나가면
{
if (Check() == false)
{
return;
}
stoneHelopOb.gameObject.SetActive(false);
//네모 흰색이 비활성화 시킵니다.
}
public void OnPointerClick(PointerEventData eventData)
//클릭시
{
if (Check() == false)
{
return;
}
stoneHelopOb.gameObject.SetActive(false);
//네모흰색 비활성화
var color = transform.GetComponent<Image>().color;
color.a = 1f;
GetComponent<Image>().color = color;
//알파값을 1로 조절해서 보이도록 합니다.
if (omokManager.inst.IsBlackTurn)
{
icon.sprite = omokManager.inst.dollImg[0];
//블랙턴이라면 이미지를 블랙으로
}
else
{
icon.sprite = omokManager.inst.dollImg[1];
//흰턴이라면 이미지를 흰색돌로
}
omokManager.inst.BallClick(row, column);
//해당 좌표를 클릭했다고 해당 매니저에게 보냅니다.
}
public void ballClick(int num)
//num이 1이라면 흑돌이 채워지고 2면 백돌이 3이라면 X표시가 표시됩니다.
{
if (num == 1)
{
var color = transform.GetComponent<Image>().color;
color.a = 1f;
GetComponent<Image>().color = color;
icon.sprite = omokManager.inst.dollImg[0];
}
else if (num == 2)
{
var color = transform.GetComponent<Image>().color;
color.a = 1f;
GetComponent<Image>().color = color;
icon.sprite = omokManager.inst.dollImg[1];
}
else if (num == 3)
{
noOb.SetActive(true);
omokManager.inst.noObs.Add(gameObject);
}
}
public void NoOff()
{
omokManager.inst.ball[column, row] = 0;
noOb.SetActive(false);
}
public void Clear()
//초기화 시킵니다.
{
var color = transform.GetComponent<Image>().color;
color.a = 0f;
GetComponent<Image>().color = color;
omokManager.inst.ball[column, row] = 0;
noOb.SetActive(false);
}
[ContextMenu("Do Something")]
void DoSomething()
//디버그용
{
Transform a = GameObject.Find("Grid").transform;
for (int i = 0; i <= 14; i++)
{
for (int j = 0; j <= 14; j++)
{
GameObject ob = Instantiate(gameObject, a);
ob.name = $"{i},{j}";
}
}
}
[ContextMenu("name")]
void nameset()
//디버그용
{
string[] s = gameObject.name.Split(',');
row = int.Parse(s[1]);
column = int.Parse(s[0]);
}
}
해당 돌을 클릭하면
소켓에다가 클릭했다고 이벤트만 전달만 하면 매우 쉽게 구현이 됩니다.
알고리즘은
알고리즘은 렌주룰을 적용시켰고요
백은 착수 제한이 없지만 흑은 33 6목 등 여러 가지 착수 금지가 있습니다.
고라니님 영상 참고했습니다.
https://www.youtube.com/watch?v=fy4Cq3gf8Sg
흑 차례가 되면 모든 타일을 탐색을 합니다.
해당 타일이 착수가 할 수 없다면 x를 표시하게 했습니다.
using System;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public enum ePlayType
{
None,
BLACK,
WHITE
}
public class omokManager : MonoBehaviour
{
public static omokManager inst;
public Sprite[] dollImg; // 이미지 입니다. 0이 흑돌 이미지,1 흰돌 이미지
public ePlayType myPlayType; // 자신이 흰색인지 흑돌인지
public bool IsPlay = false; // 플레이중
public readonly int SIZE = 15; // 바둑판 사이즈
public bool IsBlackTurn = false; // 턴
public List<GameObject> noObs;
//X표시할 때 담았다가 지울 때 리스트안에 있는 X자를 다 지우면 되니까
public int[,] ball = new int[,]
//좌표 1 흑돌 ,2 흰돌 , 3 X자 표시
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
public GameObject[] ballOb; //돌을 담습니다. 미리 담습니다 15x15 225개
public GameObject ballScan(int row, int column)
//해당 돌 GameObject를 찾는 함수
{
return ballOb[row + (column * SIZE)];
}
private void Awake()
{
inst = this;
}
private void Start()
{
SocketManager.inst.socket.OnUnityThread("GameEnd", data =>
//게임이 끝나면 발생하는 함수
{
String victory = data.GetValue(0).GetString();
String defeat = data.GetValue(1).GetString();
if (GameManager.inst.nickName == victory)
{
GameManager.inst.chatManager.Victory();
GameManager.inst.Warning("승리 했습니다.");
}
else if (GameManager.inst.nickName == defeat)
{
GameManager.inst.chatManager.Defeat();
GameManager.inst.Warning("패배 했습니다.");
}
GameManager.inst.chatManager.gameInfo.text = $"{victory}님께서 승리 했습니다.";
IsPlay = false;
myPlayType = ePlayType.None;
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
GameManager.inst.chatManager.startBtn.SetActive(true);
}
});
SocketManager.inst.socket.OnUnityThread("Turn", data =>
//턴을 넘기는 함수
{
int row = data.GetValue(1).GetInt32();
int column = data.GetValue(2).GetInt32();
if (data.GetValue(0).GetString() == "Black")
{
ball[column, row] = 1;
ballScan(row, column).GetComponent<omokClick>().ballClick(1);
}
else
{
ball[column, row] = 2;
ballScan(row, column).GetComponent<omokClick>().ballClick(2);
}
GameManager.inst.chatManager.gameInfo.text = $"{data.GetValue(3).GetString()}님의 차례입니다.";
TurnChange();
});
SocketManager.inst.socket.OnUnityThread("GameStart", data =>
//게임을 시작합니다.
{
GameStart(data.GetValue(0).GetInt32());
});
}
public void StartBtn()
//게임시작 버튼을 누르면 발생하는 함수
{
if (GameManager.inst.Player1 == GameManager.inst.nickName && GameManager.inst.Player2 != "" && IsPlay == false)
{
GameManager.inst.chatManager.startBtn.SetActive(false);
//버튼을 숨깁니다.
int ran = Random.Range(0, 2);
//0이 나오면 1번이 흑 2번이 백
//1이 나오면 1번이 백 2번이 흑
SocketManager.inst.socket.Emit("GameStart", ran);
}
}
public void StoneSetting()
//중간에 관전자가 들어오면 현재 돌 좌표들을 전부 받아서 셋팅하도록 합니다.
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] == 1 || ball[i, j] == 2)
{
ballScan(j, i).GetComponent<omokClick>().ballClick(ball[i, j]);
}
}
}
}
void GameStart(int num)
//게임 시작시 초기화를 전부 시키고 게임 시작합니다.
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] == 0)
{
continue;
}
ballScan(j, i).GetComponent<omokClick>().Clear();
}
}
IsPlay = true;
IsBlackTurn = true;
if (num == 0)
{
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
myPlayType = ePlayType.BLACK;
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
myPlayType = ePlayType.WHITE;
}
GameManager.inst.chatManager.gameInfo.text = $"{GameManager.inst.Player1}님의 차례입니다.";
}
else
{
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
myPlayType = ePlayType.WHITE;
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
myPlayType = ePlayType.BLACK;
}
GameManager.inst.chatManager.gameInfo.text = $"{GameManager.inst.Player2}님의 차례입니다.";
}
}
public void BallClick(int row, int column)
//돌 클릭 이벤트
{
if (IsBlackTurn)
{
ball[column, row] = 1;
//해당 좌표 설정
}
else
{
ball[column, row] = 2;
//해당 좌표 설정
}
if (VictoryCheck(row, column))
//돌 클릭하고 게임이 끝났는지 파악합니다
{
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
SocketManager.inst.socket.Emit("GameEnd", GameManager.inst.Player1, GameManager.inst.Player2);
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
SocketManager.inst.socket.Emit("GameEnd", GameManager.inst.Player2, GameManager.inst.Player1);
}
//첫번째 인자는 승자 두번째는 패자
}
else
{
//게임이 끝난게 아니면 턴을 넘깁니다.
string enemyName = GameManager.inst.Player1 == GameManager.inst.nickName
? GameManager.inst.Player2
: GameManager.inst.Player1;
if (IsBlackTurn)
{
SocketManager.inst.socket.Emit("Turn", "Black", row, column, enemyName);
}
else
{
SocketManager.inst.socket.Emit("Turn", "White", row, column, enemyName);
}
GameManager.inst.chatManager.gameInfo.text = $"{enemyName}님의 차례입니다.";
TurnChange();
}
}
bool VictoryCheck(int row, int column)
{
int ballNum = 1;
//흑은 1번
if (!IsBlackTurn)
{
ballNum = 2;
//흰돌은 2번
}
if (FiveCheck(ballNum))
//5개가 있는지 파악합니다.
{
return true;
}
return false;
}
bool InRange(params int[] v)
//범위가 벗어나면 오류가 뜨기때문에 검사합니다.
{
for (int i = 0; i < v.Length; i++)
if (!(v[i] >= 0 && v[i] < SIZE))
return false;
return true;
}
bool FiveCheck(int ballNum)
{
//완전 탐색합니다.
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] != ballNum)
{
continue;
}
//자신의 돌
//→
//왼쪽으로 돌을 확인합니다.
if (InRange(j + 4) && ball[i, j + 1] == ballNum && ball[i, j + 2] == ballNum &&
ball[i, j + 3] == ballNum && ball[i, j + 4] == ballNum)
{
return true;
}
//↓
//아래칸으로 돌을 확인합니다.
else if (InRange(i + 4) && ball[i + 1, j] == ballNum && ball[i + 2, j] == ballNum &&
ball[i + 3, j] == ballNum && ball[i + 4, j] == ballNum)
{
return true;
}
//↘
//대각선아래로 돌을 확인합니다.
else if (InRange(i + 4, j + 4) && ball[i + 1, j + 1] == ballNum && ball[i + 2, j + 2] == ballNum &&
ball[i + 3, j + 3] == ballNum && ball[i + 4, j + 4] == ballNum)
{
return true;
}
//↙
//왼쪽대각선아래로 돌을 확인합니다.
else if (InRange(i + 4, j - 4) && ball[i + 1, j - 1] == ballNum && ball[i + 2, j - 2] == ballNum &&
ball[i + 3, j - 3] == ballNum && ball[i + 4, j - 4] == ballNum)
{
return true;
}
}
}
return false;
}
public void omokCheck()
{
//렌주룰 이기때문에
//흑만 검사합니다
if (myPlayType != ePlayType.BLACK)
{
return;
}
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
SixCheck(i, j);
}
}
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] != 0)
{
continue;
}
ThreeThreeCheck(i, j);
}
}
}
void SixCheck(int column, int row)
//해당 좌표가 6목이라면 X표시를 합니다.
{
if (ball[column, row] != 0)
{
return;
}
//0번만 확인합니다.
ball[column, row] = 1;
//미리 흑돌을 깔고 6목인지 확인합니다.
//완전탐색을 합니다.
int ballNum = 1;
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] != 1)
{
continue;
}
//→
if (InRange(j + 5) && ball[i, j + 1] == ballNum && ball[i, j + 2] == ballNum &&
ball[i, j + 3] == ballNum && ball[i, j + 4] == ballNum && ball[i, j + 5] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
//↓
else if (InRange(i + 5) && ball[i + 1, j] == ballNum && ball[i + 2, j] == ballNum &&
ball[i + 3, j] == ballNum && ball[i + 4, j] == ballNum && ball[i + 5, j] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
//↘
else if (InRange(i + 5, j + 5) && ball[i + 1, j + 1] == ballNum && ball[i + 2, j + 2] == ballNum &&
ball[i + 3, j + 3] == ballNum && ball[i + 4, j + 4] == ballNum &&
ball[i + 5, j + 5] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
//↙
else if (InRange(i + 5, j - 5) && ball[i + 1, j - 1] == ballNum && ball[i + 2, j - 2] == ballNum &&
ball[i + 3, j - 3] == ballNum && ball[i + 4, j - 4] == ballNum &&
ball[i + 5, j - 5] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
}
}
ball[column, row] = 0;
//아무것도 아니라면 다시 0으로 돌림.
}
public void NoClear()
//X표시를 전부 지웁니다.
{
if (myPlayType != ePlayType.BLACK)
{
return;
}
for (int i = 0; i < noObs.Count; i++)
{
if (noObs[i].TryGetComponent(out omokClick click))
{
click.NoOff();
}
}
noObs.Clear();
}
public void TurnChange()
{
IsBlackTurn = !IsBlackTurn;
if (IsBlackTurn)
{
//흑턴
omokCheck();
//착수 금지 표시를 합니다.
}
else
{
//백턴
NoClear();
//본인의 X표시를 전부 지웁니다.
}
}
void ThreeThreeCheck(int i, int j)
{
if (ball[i, j] != 0)
{
return;
}
//다른 돌이 있다면 넘어감
ball[i, j] = 1;
int ThreeValue = 0;
if (FiveCheck(1))
//해당자리가 5오목이라면 착수금지가 사라짐.
{
ball[i, j] = 0;
return;
}
//→
if (ThreeCheck(i, j - 4, i, j - 3, i, j - 2, i, j - 1, i, j + 1, i, j + 2, i, j + 3, i, j + 4)) ++ThreeValue;
//↓
if (ThreeCheck(i - 4, j, i - 3, j, i - 2, j, i - 1, j, i + 1, j, i + 2, j, i + 3, j, i + 4, j)) ++ThreeValue;
//↘
if (ThreeCheck(i - 4, j - 4, i - 3, j - 3, i - 2, j - 2, i - 1, j - 1, i + 1, j + 1, i + 2, j + 2, i + 3, j + 3,
i + 4, j + 4)) ++ThreeValue;
//↙
if (ThreeCheck(i + 4, j - 4, i + 3, j - 3, i + 2, j - 2, i + 1, j - 1, i - 1, j + 1, i - 2, j + 2, i - 3, j + 3,
i - 4, j + 4)) ++ThreeValue;
if (ThreeValue >= 2)
{
//착수금지라면 X표시를합니다.
ball[i, j] = 3;
ballScan(j, i).GetComponent<omokClick>().ballClick(3);
}
else
{
//아니라면 원래대로 돌림
ball[i, j] = 0;
}
}
bool ThreeCheck(int im4, int jm4, int im3, int jm3, int im2, int jm2, int im1, int jm1, int ip1, int jp1, int ip2,
int jp2, int ip3, int jp3, int ip4, int jp4)
{
if (InRange(im4, jm4, ip1, jp1))
if (ball[im4, jm4] == 0 && ball[im3, jm3] == 1 && ball[ip1, jp1] == 0)
{
if (ball[im2, jm2] == 1 && ball[im1, jm1] == 0) return true;
if (ball[im2, jm2] == 0 && ball[im1, jm1] == 1) return true;
}
if (InRange(im3, jm3, ip1, jp1))
if (ball[im3, jm3] == 0 && ball[im2, jm2] == 1 && ball[im1, jm1] == 1 && ball[ip1, jp1] == 0)
return true;
if (InRange(im3, jm3, ip2, jp2))
if (ball[im3, jm3] == 0 && ball[im2, jm2] == 1 && ball[im1, jm1] == 0 && ball[ip1, jp1] == 1 &&
ball[ip2, jp2] == 0)
return true;
if (InRange(im2, jm2, ip2, jp2)) // 중앙
if (ball[im2, jm2] == 0 && ball[im1, jm1] == 1 && ball[ip1, jp1] == 1 && ball[ip2, jp2] == 0)
return true;
if (InRange(im2, jm2, ip3, jp3))
if (ball[im2, jm2] == 0 && ball[im1, jm1] == 1 && ball[ip1, jp1] == 0 && ball[ip2, jp2] == 1 &&
ball[ip3, jp3] == 0)
return true;
if (InRange(im1, jm1, ip3, jp3))
if (ball[im1, jm1] == 0 && ball[ip1, jp1] == 1 && ball[ip2, jp2] == 1 && ball[ip3, jp3] == 0)
return true;
if (InRange(im1, jm1, ip4, jp4))
if (ball[im1, jm1] == 0 && ball[ip3, jp3] == 1 && ball[ip4, jp4] == 0)
{
if (ball[ip1, jp1] == 1 && ball[ip2, jp2] == 0) return true;
if (ball[ip1, jp1] == 0 && ball[ip2, jp2] == 1) return true;
}
return false;
}
}
중간에 들어온다면
플레이 중일 때만 현재 돌을 보내면 되기 때문에
1번 플레이어 또는 2번 플레이어 있을 때만 보냅니다.
한 곳 데이터로 모아서 보내야 하는데
임시로 여러 인자로 데이터를 보내는 방식으로 했습니다.
플레이어가 들어오면 해당 방에게 모든 플레이어에게 이벤트를 쏘고
1번 이거나 또는 1번에 아무도 없고 2번에 사람이 있으면 보내도록 했습니다.
chatmanager.cs
SocketManager.inst.socket.OnUnityThread("PlayerEnter", data =>
//중간에 들어오면 들어온사람에게 정보를 보내줍니다.
{
players = JsonConvert.DeserializeObject<String[]>(data.GetValue(0).ToString());
//기존 플레이어목록을 보내줍니다.
PlayerReSet();
if (GameManager.inst.Player1==GameManager.inst.nickName)
//1번 플레이어라면 보냄
{
string s = JsonConvert.SerializeObject(omokManager.inst.ball);
//2차원배열을 문자열로 변환
SocketManager.inst.socket.Emit("EnterFunc",s,player1.text,player2.text,player1_record.text,player2_record.text,data.GetValue(1).ToString(),omokManager.inst.IsPlay,omokManager.inst.IsBlackTurn,gameInfo.text);
}
else if (GameManager.inst.Player1 == "" && GameManager.inst.Player2 == GameManager.inst.nickName)
//1번에는 아무도없고 2번플레이어가 있다면 보냄
{
string s = JsonConvert.SerializeObject(omokManager.inst.ball);
//2차원 배열을 문자열로 변환
SocketManager.inst.socket.Emit("EnterFunc",s,player1.text,player2.text,player1_record.text,player2_record.text,data.GetValue(1).ToString(),omokManager.inst.IsPlay,omokManager.inst.IsBlackTurn,gameInfo.text);
}
});
특정 클라이언트만 받으려면
to(소켓 아이디)를 활용하면 되더라고요
server.js
socket.on('EnterFunc', (stone, player1, player2, player1_record, player2_record, id, bool1, bool2, infotext) => {
socket.to(id).emit('EnterFunc', stone, player1, player2, player1_record, player2_record, id, bool1, bool2, infotext)
})
chatManager.cs
SocketManager.inst.socket.OnUnityThread("EnterFunc", data =>
//정보 보내준거 받음
{
GameManager.inst.Player1 = data.GetValue(1).GetString();
GameManager.inst.Player2 = data.GetValue(2).GetString();
player1.text = GameManager.inst.Player1;
player2.text = GameManager.inst.Player2;
player1_record.text = data.GetValue(3).GetString();
player2_record.text = data.GetValue(4).GetString();
omokManager.inst.ball = JsonConvert.DeserializeObject<int[,]>(data.GetValue(0).ToString());
omokManager.inst.StoneSetting();
omokManager.inst.IsPlay = data.GetValue(6).GetBoolean();
omokManager.inst.IsBlackTurn = data.GetValue(7).GetBoolean();
gameInfo.text = data.GetValue(8).GetString();
});
mysql와 socket.io를 이용해서 오목을 만들었습니다.
2페이지에서 만들었던 채팅에다가 오목만 추가하는 방식을 해서
클라이언트가 정보를 보내는 걸 전달해주는 방식을 썼는데
정보는 서버가 가지고 있다가 전달해주는 방식이 좋은 거 같습니다.
깃허브 주소
https://github.com/wolstar415/unity-Omok
모든 코드 보기
server.js
const express = require('express');
var mysql = require('mysql');
const dbconfig = require('./config/config.js');
const sql = mysql.createConnection(dbconfig);
const bcrypt = require("bcrypt");
const app = express();
const http = require('http');
const server = http.createServer(app);
const port = 7777;
const { Server } = require("socket.io");
const e = require('express');
const io = new Server(server);
io.use((socket, next) => {
if (socket.handshake.query.token === "UNITY" && socket.handshake.query.version === "0.1") {
next();
} else {
next(new Error("인증 오류 "));
}
});
var Users = [];
var Rooms = [];
io.on('connection', socket => {
Users[socket.id] = {
id: socket.id,
loginID: "",
nickname: "",
Room: "",
victory: 0,
defeat: 0
}
function RoomResetGo() {
var roomcheck = [];
for (room in Rooms) {
roomcheck.push({
currentCnt: Rooms[room].currentCnt,
RoomMaxCnt: Rooms[room].maxCnt,
name: room
})
}
io.emit('RoomReset', roomcheck)
}
socket.on('LoginCheck', (id, password) => {
sql.query('SELECT * FROM users WHERE ID=?', [id], function (error, results) {
if (error) throw error;
if (results.length > 0) {
if (bcrypt.compareSync(password, results[0].PassWord)) {
//암호화된 비밀번호를 비교합니다.
var check = true;
for (var k in Users) {
if (Users[k].loginID == id)
check = false;
break;
}
//현재 접속된 아이디가 있는지 파악합니다.
if (check) {
console.log(id + ": 로그인 성공")
Users[socket.id].loginID = id;
Users[socket.id].nickname = results[0].nickName;
Users[socket.id].victory = results[0].victory
Users[socket.id].defeat = results[0].defeat
socket.emit('Login', results[0].nickName, results[0].victory, results[0].defeat)
sql.query('UPDATE users SET loginlasttime=? WHERE ID=? ', [new Date(), id], function (error, results) {
{
if (error)
console.log(error)
}
})
}
else {
//동일한 아이디가 들어왔으니 오류
console.log("중복 불가!")
socket.emit('LoginFailed2')
}
}
else {
//비밀번호가 틀리면 오류
console.log("비밀번호 틀림")
socket.emit('LoginFailed')
}
} else {
//해당 아이디가 없으면 오류
console.log("로그인 실패")
socket.emit('LoginFailed')
}
})
})
socket.on('CreateCheck', (id, password) => {
//회원가입 체크
sql.query('SELECT * FROM users WHERE ID=?', [id], function (error, results) {
//회원가입 하기전에 아이디 중복확인을 합니다.
if (error) throw error;
if (results.length > 0) {
//아이디가 이미 있다면 실패
console.log("회원가입 실패")
socket.emit('CreateFailed')
}
else {
sql.query('INSERT INTO users (ID, PassWord, createTime) VALUES(?,?,?)', [id, bcrypt.hashSync(password, 10), new Date()], function (error, results) {
if (error) {
console.log(error)
}
else {
console.log(id + ": 회원가입 성공")
socket.emit('Create')
}
})
}
})
})
socket.on('NickNameCheck', (nickName, id) => {
sql.query('SELECT * FROM users WHERE nickName=?', [nickName], function (error, results) {
//닉네임을 데이터베이스에서 찾습니다.
if (error) throw error;
if (results.length > 0) {
//해당 닉네임이있다면 오류
socket.emit('NickNameFailed')
console.log("닉네임 겹침")
} else {
sql.query('UPDATE users SET nickName=? WHERE ID=? ', [nickName, id], function (error, results) {
//데이터베이스에 수정합니다.
{
if (error) {
console.log(error)
}
else {
Users[socket.id].nickname = nickName;
console.log(nickName + ": 닉네임 설정 성공")
socket.emit('NickName')
}
}
})
}
})
})
socket.on('JoinRoomCheck', (roomname) => {
if (roomname in Rooms && Rooms[roomname].currentCnt < Rooms[roomname].maxCnt) {
socket.join(roomname)
socket.emit('Join', roomname)
Users[socket.id].Room = roomname
Rooms[roomname].currentCnt++
var check = []
socket.adapter.rooms.get(roomname).forEach((a) => {
check.push(Users[a].nickname)
})
socket.to(roomname).emit('PlayerEnter', check, socket.id)
RoomResetGo()
}
else {
socket.emit('JoinFailed')
}
})
socket.on('EnterFunc', (stone, player1, player2, player1_record, player2_record, id, bool1, bool2, infotext) => {
socket.to(id).emit('EnterFunc', stone, player1, player2, player1_record, player2_record, id, bool1, bool2, infotext)
})
socket.on('CreateRoomCheck', (data, data2) => {
if (data in Rooms) {
//방이 있는지 없는지 확인
console.log(" 방이름 겹침!")
socket.emit('CreateRoomFailed')
//방생성 실패
}
else {
//방생성 성공
socket.join(data);
//들어갑니다.
Users[socket.id].Room = data
Rooms[data] = {
currentCnt: 1,
maxCnt: Number(data2),
Player1: "",
Player2: "",
}
console.log(data + ": 방진입 성공!")
socket.emit('CreateRoom')
//성공했다고 이벤트를 보냅니다.
RoomResetGo()
//방 목록을 전부 보내는 이벤트를 실행합니다.
}
})
socket.on('LeaveRoomCheck', (data, data2) => {
//방을 나갑니다
socket.leave(data)
//leave를 사용합니다.
if (Number(data2) <= 1) {
//현재 방인원이 1이라면 삭제를 시킵니다.
delete Rooms[Users[socket.id].Room]
}
else {
Rooms[data].currentCnt--
//방 인원 하나 뺍니다
var check = []
socket.adapter.rooms.get(data).forEach((a) => {
check.push(Users[a].nickname)
})
//현재 방인원 플레이어 목록을 갱신 시켜줍니다.
if (Rooms[Users[socket.id].Room].Player1 == Users[socket.id].nickname) {
Rooms[Users[socket.id].Room].Player1 = "";
}
else if (Rooms[Users[socket.id].Room].Player2 == Users[socket.id].nickname) {
Rooms[Users[socket.id].Room].Player2 = "";
}
var player1name = ""
var player2name = ""
var player1record = ""
var player2record = ""
if (Rooms[Users[socket.id].Room].Player1 != "") {
for (var a in Users) {
if (Rooms[Users[socket.id].Room].Player1 == Users[a].nickname) {
player1name = Users[a].nickname
player1record = Users[a].victory + "승 " + Users[a].defeat + "패"
break;
}
}
}
if (Rooms[Users[socket.id].Room].Player2 != "") {
for (var a in Users) {
if (Rooms[Users[socket.id].Room].Player2 == Users[a].nickname) {
player2name = Users[a].nickname
player2record = Users[a].victory + "승 " + Users[a].defeat + "패"
break;
}
}
}
socket.to(data).emit('PlayerReset', check, player1name, player2name, player1record, player2record)
}
RoomResetGo()
socket.emit('LeaveRoom')
Users[socket.id].Room = ""
})
socket.on('RoomListCheck', (data) => {
if (socket.adapter.rooms.size == 1) {
return
}
var roomcheck = [];
for (room in Rooms) {
roomcheck.push({
currentCnt: Rooms[room].currentCnt,
RoomMaxCnt: Rooms[room].maxCnt,
name: room
})
}
console.log(roomcheck)
socket.emit('RoomList', roomcheck)
})
socket.on('Chat', (nick, text, room) => {
//채팅을 보냅니다.
console.log(nick + ": " + text)
socket.to(room).emit('ChatGet', nick, text)
//보인을 제외한 방에 존재한 사람들에게 보냅니다.
})
socket.on('ChatCheck', (data) => {
var check = []
socket.adapter.rooms.get(data).forEach((a) => {
check.push(Users[a].nickname)
})
//방안에있는 플레이어들의 목록을 불러옵니다.
socket.emit('ChatOn', check)
})
console.log("연결함 : " + socket.id);
function playercheckFunc(roomname) {
var p1 = {
name: "",
victory: 0,
defeat: 0
}
var p2 = {
name: "",
victory: 0,
defeat: 0
}
if (Rooms[roomname].Player1 == "") {
io.to(roomname).emit('PlayerChagne1', "")
}
else {
sql.query('SELECT * FROM users WHERE nickName=?', [Rooms[roomname].Player1], function (error, results) {
if (error) throw error;
if (results.length > 0) {
p1.name = Rooms[roomname].Player1
p1.victory = results[0].victory
p1.defeat = results[0].defeat
io.to(roomname).emit('PlayerChagne1', p1)
}
})
}
if (Rooms[roomname].Player2 == "") {
io.to(roomname).emit('PlayerChagne2', "")
}
else {
sql.query('SELECT * FROM users WHERE nickName=?', [Rooms[roomname].Player2], function (error, results) {
if (error) throw error;
if (results.length > 0) {
p2.name = Rooms[roomname].Player2
p2.victory = results[0].victory
p2.defeat = results[0].defeat
io.to(roomname).emit('PlayerChagne2', p2)
}
})
}
}
socket.on('PlayerCheck', (roomname, player1, player2) => {
Rooms[roomname].Player1 = player1
Rooms[roomname].Player2 = player2
playercheckFunc(roomname)
})
socket.on('Victory', (id, cnt, roomname) => {
sql.query('UPDATE users SET victory=? WHERE ID=? ', [cnt, id], function (error, results) {
{
if (error) {
console.log(error)
}
else {
console.log(id + ": 승리 추가")
playercheckFunc(roomname)
}
}
})
})
socket.on('GameStart', (ran) => {
io.to(Users[socket.id].Room).emit('GameStart', ran)
})
socket.on('Defeat', (id, cnt, roomname) => {
sql.query('UPDATE users SET defeat=? WHERE ID=? ', [cnt, id], function (error, results) {
{
if (error) {
console.log(error)
}
else {
console.log(id + ": 패배 추가 " + cnt)
playercheckFunc(roomname)
}
}
})
})
socket.on('Turn', (stone, row, column, enemyName) => {
socket.to(Users[socket.id].Room).emit('Turn', stone, row, column, enemyName)
})
socket.on('GameEnd', (victory, defeat) => {
io.to(Users[socket.id].Room).emit('GameEnd', victory, defeat)
})
socket.on('disconnect', zz => {
console.log("연결끊김 : " + socket.id);
//console.log(Users[socket.id].Room);
if (Users[socket.id].Room != "") {
//해당 유저가 방안이라면
if (Rooms[Users[socket.id].Room].currentCnt == 1) {
//인원이 1이라면 삭제
delete Rooms[Users[socket.id].Room]
}
else {
Rooms[Users[socket.id].Room].currentCnt--
var nick = Users[socket.id].nickname
if (Rooms[Users[socket.id].Room].Player1 == nick) {
Rooms[Users[socket.id].Room].Player1 = ""
//socket.to(Users[socket.id].Room).emit('LeavePlayer', nick)
}
else if (Rooms[Users[socket.id].Room].Player2 == nick) {
Rooms[Users[socket.id].Room].Player2 = ""
}
console.log(Rooms[Users[socket.id].Room].Player1)
console.log(Rooms[Users[socket.id].Room].Player2)
//룸 인원 빼기
var check = []
socket.adapter.rooms.get(Users[socket.id].Room).forEach((a) => {
check.push(Users[a].nickname)
})
var player1name = ""
var player2name = ""
var player1record = ""
var player2record = ""
if (Rooms[Users[socket.id].Room].Player1 != "") {
for (var a in Users) {
if (Rooms[Users[socket.id].Room].Player1 == Users[a].nickname) {
player1name = Users[a].nickname
player1record = Users[a].victory + "승 " + Users[a].defeat + "패"
break;
}
}
}
if (Rooms[Users[socket.id].Room].Player2 != "") {
for (var a in Users) {
if (Rooms[Users[socket.id].Room].Player2 == Users[a].nickname) {
player2name = Users[a].nickname
player2record = Users[a].victory + "승 " + Users[a].defeat + "패"
break;
}
}
}
socket.to(Users[socket.id].Room).emit('PlayerReset', check, player1name, player2name, player1record, player2record)
//그 안에있는 사람들 플레이어 목록 갱신
RoomResetGo()
//playercheckFunc(Users[socket.id].Room)
//방 리셋
}
}
delete Users[socket.id]
//유저 정보 삭제
})
});
server.listen(port, () => {
console.log('listening on *:' + port);
});
function victoryAdd(id) {
Users[id].victory++
sql.query('UPDATE users SET victory=? WHERE ID=? ', [Users[id].victory, Users[id].nickname], function (error, results) {
{
if (error)
console.log(error)
}
})
}
function defeatAdd(id) {
Users[id].defeat++
sql.query('UPDATE users SET defeat=? WHERE ID=? ', [Users[id].defeat, Users[id].nickname], function (error, results) {
{
if (error)
console.log(error)
}
})
}
GameManager.cs
using System;
using TMPro;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager inst;
public String ID; //아이디
public String nickName; //닉네임
public int victory;
public int defeat;
[SerializeField] GameObject warningob;
[SerializeField] TextMeshProUGUI warningText;
public String room; // 현재 접속한 방이름
[Range(2, 8)] public int maxRoom = 2; // 방옵션
public bool IsChat = false; //현재 채팅중인지 아닌지
public GameObject loginOb;
public GameObject IdCreateOb;
public GameObject nickNameSetob;
public GameObject joinOb;
public GameObject chatOb;
public GameObject loadingOb;
public String Player1;
public String Player2;
//public GameObject loginWarningOb;
//public GameObject roomWarningOb;
public ChatManager chatManager;
public LobyManager lobyManager;
//UI들
private void Awake()
{
inst = this;
}
public void Warning(string s)
{
warningob.SetActive(true);
warningText.text = s;
}
}
LoginManager.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class LoginManager : MonoBehaviour
{
public TextMeshProUGUI nickname;
public TMP_InputField loginInputField; //아이디 필드
public TMP_InputField passWordInputField; //비밀번호 필드
public TMP_InputField loginCheckInputField; //회원가입 아이디 필드
public TMP_InputField passWordCheck1InputField; //회원가입 비밀번호 필드
public TMP_InputField passWordCheck2InputField; //회원가입 비밀번호 필드
public TMP_InputField nickNameInputField; //닉네임 필드
public void LoginBtn()
//접속 버튼 누르면 실행
{
if (loginInputField.text == "" || passWordInputField.text == "")
{
return;
}
GameManager.inst.loadingOb.SetActive(true);
SocketManager.inst.socket.Emit("LoginCheck", loginInputField.text, passWordInputField.text);
}
public void CreateBtn()
//접속 버튼 누르면 실행
{
if (loginCheckInputField.text == "" || passWordCheck1InputField.text == "" ||
passWordCheck2InputField.text == "")
{
return;
}
if (passWordCheck1InputField.text != passWordCheck2InputField.text)
{
GameManager.inst.Warning("비밀번호가 맞지 않습니다.");
return;
}
GameManager.inst.loadingOb.SetActive(true);
SocketManager.inst.socket.Emit("CreateCheck", loginCheckInputField.text, passWordCheck1InputField.text);
}
public void NickNameBtn()
//접속 버튼 누르면 실행
{
if (nickNameInputField.text == "")
{
return;
}
GameManager.inst.loadingOb.SetActive(true);
SocketManager.inst.socket.Emit("NickNameCheck", nickNameInputField.text, GameManager.inst.ID);
}
// Start is called before the first frame update
void Start()
{
loginInputField.Select();
SocketManager.inst.socket.OnUnityThread("Login", data =>
{
GameManager.inst.nickName = loginInputField.text;
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.loginOb.SetActive(false);
GameManager.inst.victory = data.GetValue(1).GetInt32();
GameManager.inst.defeat = data.GetValue(2).GetInt32();
GameManager.inst.lobyManager.recordText.text = $"{GameManager.inst.victory}승 {GameManager.inst.defeat}패";
GameManager.inst.ID = loginInputField.text;
GameManager.inst.nickName = data.GetValue(0).GetString();
if (GameManager.inst.nickName == "")
{
nickNameInputField.Select();
GameManager.inst.nickNameSetob.SetActive(true);
}
else
{
GameManager.inst.joinOb.SetActive(true);
SocketManager.inst.socket.Emit("RoomListCheck", null);
nickname.text = GameManager.inst.nickName;
}
});
SocketManager.inst.socket.OnUnityThread("LoginFailed", data =>
{
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.Warning("아이디와 비밀번호를 확인해주세요");
});
SocketManager.inst.socket.OnUnityThread("LoginFailed2", data =>
{
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.Warning("이미 들어와있습니다.");
});
SocketManager.inst.socket.OnUnityThread("Create", data =>
{
loginInputField.text = loginCheckInputField.text;
loginCheckInputField.text = "";
passWordCheck1InputField.text = "";
passWordCheck2InputField.text = "";
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.IdCreateOb.SetActive(false);
GameManager.inst.loginOb.SetActive(true);
passWordInputField.text = "";
passWordInputField.Select();
});
SocketManager.inst.socket.OnUnityThread("CreateFailed", data =>
{
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.Warning("해당 아이디가 존재합니다.");
});
SocketManager.inst.socket.OnUnityThread("NickName", data =>
{
GameManager.inst.nickName = nickNameInputField.text;
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.nickNameSetob.SetActive(false);
GameManager.inst.joinOb.SetActive(true);
nickname.text = GameManager.inst.nickName;
});
SocketManager.inst.socket.OnUnityThread("NickNameFailed", data =>
{
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.Warning("해당 닉네임이 존재합니다.");
});
}
public void OnEndEditEventMethod(int i)
{
switch (i)
{
case 1:
if (Input.GetKeyDown(KeyCode.Return))
{
LoginBtn();
}
break;
case 2:
if (Input.GetKeyDown(KeyCode.Return))
{
CreateBtn();
}
break;
default:
break;
}
}
// Update is called once per frame
void Update()
{
if (GameManager.inst.IdCreateOb.activeSelf || GameManager.inst.loginOb.activeSelf)
{
if (Input.GetKeyDown(KeyCode.Tab))
{
if (GameManager.inst.IdCreateOb.activeSelf == false)
{
if (loginInputField.isFocused)
{
passWordInputField.Select();
}
else
{
loginInputField.Select();
}
}
else
{
if (loginCheckInputField.isFocused)
{
passWordCheck1InputField.Select();
}
else if (passWordCheck1InputField.isFocused)
{
passWordCheck2InputField.Select();
}
else
{
loginCheckInputField.Select();
}
}
}
}
}
}
LobyManager.cs
using System.Collections.Generic;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
[System.Serializable]
public class RoomInfo
{
public int currentCnt;
public int RoomMaxCnt;
public string name;
}
public class LobyManager : MonoBehaviour
{
public TMP_InputField createInputField;
public RoomInfo[] roomsInfo;
public Transform roomParent;
public GameObject roomPrefab;
public List<GameObject> roomobs;
public TextMeshProUGUI recordText;
public void CreateBtn()
//방생성 버튼시 실행하는 함수
{
if (createInputField.text == "")
{
return;
}
GameManager.inst.loadingOb.SetActive(true);
SocketManager.inst.socket.Emit("CreateRoomCheck", createInputField.text, GameManager.inst.maxRoom);
}
public void OnEndEditEventMethod()
{
if (Input.GetKeyDown(KeyCode.Return))
{
CreateBtn();
}
}
private void Start()
{
SocketManager.inst.socket.OnUnityThread("CreateRoom", data =>
{
GameManager.inst.room = createInputField.text;
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.joinOb.SetActive(false);
GameManager.inst.chatOb.SetActive(true);
GameManager.inst.chatManager.ChatStart();
});
SocketManager.inst.socket.OnUnityThread("CreateRoomFailed", data =>
{
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.Warning("똑같은 이름의 방이 존재합니다.");
});
SocketManager.inst.socket.OnUnityThread("RoomReset", data =>
{
roomsInfo = JsonConvert.DeserializeObject<RoomInfo[]>(data.GetValue(0).ToString());
RoomReset();
});
SocketManager.inst.socket.OnUnityThread("RoomList", data =>
{
if (data.ToString() == "[[]]")
{
return;
}
roomsInfo = JsonConvert.DeserializeObject<RoomInfo[]>(data.GetValue(0).ToString());
RoomReset();
});
SocketManager.inst.socket.OnUnityThread("Join", data =>
{
GameManager.inst.room = data.GetValue(0).ToString();
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.joinOb.SetActive(false);
GameManager.inst.chatOb.SetActive(true);
GameManager.inst.chatManager.ChatStart();
});
SocketManager.inst.socket.OnUnityThread("JoinFailed", data =>
{
GameManager.inst.loadingOb.SetActive(false);
SocketManager.inst.socket.Emit("RoomListCheck", null);
});
}
public void MaxRoomChange(int value)
{
GameManager.inst.maxRoom = value;
}
public void JoinRoom(string name)
{
GameManager.inst.loadingOb.SetActive(true);
SocketManager.inst.socket.Emit("JoinRoomCheck", name);
}
public void RoomReset()
{
if (roomobs.Count > 0)
{
for (int i = 0; i < roomobs.Count; i++)
{
Destroy(roomobs[i]);
}
}
roomobs.Clear();
for (int i = 0; i < roomsInfo.Length; i++)
{
if (roomsInfo[i].currentCnt < roomsInfo[i].RoomMaxCnt)
{
GameObject room = Instantiate(roomPrefab, roomParent);
var roominfo = room.GetComponent<RoomPrefab>();
roominfo.nameText.text = roomsInfo[i].name;
roominfo.name = roomsInfo[i].name;
roominfo.cntText.text = $"{roomsInfo[i].currentCnt}/{roomsInfo[i].RoomMaxCnt}";
roomobs.Add(room);
}
}
}
}
ChatManager.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
public class ChatManager : MonoBehaviour
{
[SerializeField] private GameObject textPrefab; // 채팅프리팹
[SerializeField] private Transform textParent; //채팅 생성시킬곳
[SerializeField] private TMP_InputField inputField; //채팅 입력칸
[SerializeField] private TextMeshProUGUI roomNameText; //방이름
[SerializeField] private Transform playerparent; //플레이어 목록
[SerializeField] private string[] players; //플레이어 목록들
[SerializeField] private List<GameObject> textobs;
[SerializeField] private TextMeshProUGUI player1;
[SerializeField] private TextMeshProUGUI player2;
[SerializeField] private TextMeshProUGUI player1_record;
[SerializeField] private TextMeshProUGUI player2_record;
public GameObject startBtn;
public TextMeshProUGUI gameInfo;
private void Start()
{
SocketManager.inst.socket.OnUnityThread("ChatOn", data =>
//채팅을 시작합니다.
{
GameManager.inst.loadingOb.SetActive(false);
//로딩 끄게합니다.
players = JsonConvert.DeserializeObject<String[]>(data.GetValue(0).ToString());
PlayerReSet();
//플레이어 목록을 받고 설정합니다.
});
SocketManager.inst.socket.OnUnityThread("PlayerReset", data =>
//플레이어 목록을 갱신합니다. 나가거나 들어올 때 방안에있는 사람들이 받는 이벤트입니다.
{
players = JsonConvert.DeserializeObject<String[]>(data.GetValue(0).ToString());
GameManager.inst.Player1 = data.GetValue(1).GetString();
GameManager.inst.Player2 = data.GetValue(2).GetString();
player1.text = GameManager.inst.Player1;
player2.text = GameManager.inst.Player2;
player1_record.text = data.GetValue(3).GetString();
player2_record.text = data.GetValue(4).GetString();
PlayerReSet();
if (omokManager.inst.IsPlay)
{
if (GameManager.inst.Player1 == "" || GameManager.inst.Player2 == "")
{
gameInfo.text = "게임시작 전";
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
Victory();
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
Victory();
}
}
}
});
SocketManager.inst.socket.OnUnityThread("LeaveRoom", data =>
//방을 나갑니다
{
GameManager.inst.loadingOb.SetActive(false);
GameManager.inst.lobyManager.RoomReset();
for (int i = 0; i < omokManager.inst.SIZE; i++)
{
for (int j = 0; j < omokManager.inst.SIZE; j++)
{
if (omokManager.inst.ball[i, j] == 0)
{
continue;
}
omokManager.inst.ballScan(j, i).GetComponent<omokClick>().Clear();
}
}
omokManager.inst.myPlayType = ePlayType.None;
//방을 나갔으니 방갱신을 해야합니다.
});
SocketManager.inst.socket.OnUnityThread("ChatGet",
data => { ChatGet(data.GetValue(0).ToString(), data.GetValue(1).ToString()); });
//채팅을 받고 올리는 이벤트입니다.
SocketManager.inst.socket.OnUnityThread("PlayerChagne1", data =>
{
if (data.GetValue(0).ToString() == "")
{
GameManager.inst.Player1 = "";
player1.text = "";
player1_record.text = "";
}
else
{
GameManager.inst.Player1 = data.GetValue(0).GetProperty("name").ToString();
player1.text = GameManager.inst.Player1;
int victory = data.GetValue(0).GetProperty("victory").GetInt32();
int defeat = data.GetValue(0).GetProperty("defeat").GetInt32();
player1_record.text = $"{victory}승 {defeat}패";
}
});
SocketManager.inst.socket.OnUnityThread("PlayerChagne2", data =>
{
if (data.GetValue(0).ToString() == "")
{
GameManager.inst.Player2 = "";
player2.text = "";
player2_record.text = "";
}
else
{
GameManager.inst.Player2 = data.GetValue(0).GetProperty("name").ToString();
player2.text = GameManager.inst.Player2;
int victory = data.GetValue(0).GetProperty("victory").GetInt32();
int defeat = data.GetValue(0).GetProperty("defeat").GetInt32();
player2_record.text = $"{victory}승 {defeat}패";
}
});
SocketManager.inst.socket.OnUnityThread("PlayerEnter", data =>
//중간에 들어오면 들어온사람에게 정보를 보내줍니다.
{
players = JsonConvert.DeserializeObject<String[]>(data.GetValue(0).ToString());
//기존 플레이어목록을 보내줍니다.
PlayerReSet();
if (GameManager.inst.Player1 == GameManager.inst.nickName)
//1번 플레이어라면 보냄
{
string s = JsonConvert.SerializeObject(omokManager.inst.ball);
//2차원배열을 문자열로 변환
SocketManager.inst.socket.Emit("EnterFunc", s, player1.text, player2.text, player1_record.text,
player2_record.text, data.GetValue(1).ToString(), omokManager.inst.IsPlay,
omokManager.inst.IsBlackTurn, gameInfo.text);
}
else if (GameManager.inst.Player1 == "" && GameManager.inst.Player2 == GameManager.inst.nickName)
//1번에는 아무도없고 2번플레이어가 있다면 보냄
{
string s = JsonConvert.SerializeObject(omokManager.inst.ball);
//2차원 배열을 문자열로 변환
SocketManager.inst.socket.Emit("EnterFunc", s, player1.text, player2.text, player1_record.text,
player2_record.text, data.GetValue(1).ToString(), omokManager.inst.IsPlay,
omokManager.inst.IsBlackTurn, gameInfo.text);
}
});
SocketManager.inst.socket.OnUnityThread("EnterFunc", data =>
//정보 보내준거 받음
{
GameManager.inst.Player1 = data.GetValue(1).GetString();
GameManager.inst.Player2 = data.GetValue(2).GetString();
player1.text = GameManager.inst.Player1;
player2.text = GameManager.inst.Player2;
player1_record.text = data.GetValue(3).GetString();
player2_record.text = data.GetValue(4).GetString();
omokManager.inst.ball = JsonConvert.DeserializeObject<int[,]>(data.GetValue(0).ToString());
omokManager.inst.StoneSetting();
omokManager.inst.IsPlay = data.GetValue(6).GetBoolean();
omokManager.inst.IsBlackTurn = data.GetValue(7).GetBoolean();
gameInfo.text = data.GetValue(8).GetString();
});
}
public void LeaveBtn()
//나가기 버튼을 누를 때
{
if (omokManager.inst.IsPlay && omokManager.inst.myPlayType != ePlayType.None)
{
Defeat();
}
SocketManager.inst.socket.Emit("LeaveRoomCheck", GameManager.inst.room, players.Length);
GameManager.inst.chatOb.SetActive(false);
GameManager.inst.joinOb.SetActive(true);
GameManager.inst.loadingOb.SetActive(true);
GameManager.inst.room = "";
GameManager.inst.IsChat = false;
}
public void PlayerMove(int idx)
{
if (idx == 1)
{
if (GameManager.inst.Player1 != "")
{
return;
}
if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
GameManager.inst.Player2 = "";
}
GameManager.inst.Player1 = GameManager.inst.nickName;
startBtn.SetActive(true);
}
else
{
if (GameManager.inst.Player2 != "")
{
return;
}
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
GameManager.inst.Player1 = "";
startBtn.SetActive(false);
}
GameManager.inst.Player2 = GameManager.inst.nickName;
}
SocketManager.inst.socket.Emit("PlayerCheck", GameManager.inst.room, GameManager.inst.Player1,
GameManager.inst.Player2);
}
private void PlayerReSet()
{
for (int i = 0; i < 8; i++)
{
playerparent.GetChild(i).GetComponent<TextMeshProUGUI>().text = "";
}
for (int i = 0; i < players.Length; i++)
{
playerparent.GetChild(i).GetComponent<TextMeshProUGUI>().text = players[i];
}
}
public void OnEndEditEventMethod()
{
if (Input.GetKeyDown(KeyCode.Return))
{
UpdateChat();
}
}
public void ChatStart()
//채팅 실행
{
GameManager.inst.Player1 = "";
GameManager.inst.Player2 = "";
player1.text = "";
player2.text = "";
player1_record.text = "";
player2_record.text = "";
gameInfo.text = "게임시작 전";
startBtn.SetActive(false);
roomNameText.text = GameManager.inst.room;
GameManager.inst.IsChat = true;
for (int i = 0; i < 8; i++)
{
playerparent.GetChild(i).GetComponent<TextMeshProUGUI>().text = "";
}
if (textobs.Count > 0)
{
for (int i = 0; i < textobs.Count; i++)
{
Destroy(textobs[i]);
}
}
textobs.Clear();
//기존에 있던 채팅 모두 삭제합니다.
GameManager.inst.loadingOb.SetActive(true);
//로딩
SocketManager.inst.socket.Emit("ChatCheck", GameManager.inst.room);
}
public void UpdateChat()
//채팅을 입력시 이벤트
{
if (inputField.text.Equals(""))
{
return;
}
//아무것도없다면 리턴
GameObject ob = Instantiate(textPrefab, textParent);
ob.GetComponent<TextMeshProUGUI>().text = $"<color=red>{GameManager.inst.nickName} </color>: {inputField.text}";
textobs.Add(ob);
SocketManager.inst.socket.Emit("Chat", GameManager.inst.nickName, inputField.text, GameManager.inst.room);
//딴사람들에게도 채팅내용을 받아야하니 이벤트를 보냅니다.
inputField.text = "";
}
public void ChatGet(string nickname, string text)
//다른사람들이 채팅 이벤트 받으면 생성시킵니다.
{
GameObject ob = Instantiate(textPrefab, textParent);
textobs.Add(ob);
ob.GetComponent<TextMeshProUGUI>().text = $"{nickname} : {text}";
}
private void Update()
{
if (GameManager.inst.IsChat)
{
if (Input.GetKeyDown(KeyCode.Return) && inputField.isFocused == false)
{
inputField.ActivateInputField();
}
}
}
public void Victory()
{
GameManager.inst.victory++;
SocketManager.inst.socket.Emit("Victory", GameManager.inst.ID, GameManager.inst.victory, GameManager.inst.room);
GameManager.inst.lobyManager.recordText.text = $"{GameManager.inst.victory}승 {GameManager.inst.defeat}패";
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
startBtn.SetActive(true);
}
GameManager.inst.Warning("승리 했습니다.");
omokManager.inst.myPlayType = ePlayType.None;
omokManager.inst.IsPlay = false;
}
public void Defeat()
{
GameManager.inst.defeat++;
SocketManager.inst.socket.Emit("Defeat", GameManager.inst.ID, GameManager.inst.defeat, GameManager.inst.room);
GameManager.inst.lobyManager.recordText.text = $"{GameManager.inst.victory}승 {GameManager.inst.defeat}패";
}
}
omokClick
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class omokClick : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
[Header("세로")] public int column; //세로 ↕
[Header("가로")] public int row; //가로 ↔
[Space(20)] [SerializeField] private Image icon; //본인 Image
[SerializeField] private GameObject stoneHelopOb; //네모 흰색
[SerializeField] private GameObject noOb; // x 표시
public bool Check()
//선택을 할 수 있는지 확인하는 스크립트
{
if (omokManager.inst.IsPlay == false)
//플레이중이 아님
{
return false;
}
if (omokManager.inst.IsBlackTurn)
//블랙턴
{
if (omokManager.inst.myPlayType != ePlayType.BLACK)
//본인의 돌 타입이 블랙이 아니라면 false
{
return false;
}
}
else
{
if (omokManager.inst.myPlayType != ePlayType.WHITE)
//블랙턴이 아니고 본인의 돌 타입이 흰돌이 아니면 false
{
return false;
}
}
if (omokManager.inst.ball[column, row] != 0)
//해당 돌에 돌이 있다면 false
{
return false;
}
return true;
}
public void OnPointerEnter(PointerEventData eventData)
//마우스가 들어오면
{
if (Check() == false)
{
return;
}
stoneHelopOb.gameObject.SetActive(true);
//네모흰색이 표시되도록 합니다.
}
public void OnPointerExit(PointerEventData eventData)
//마우스가 나가면
{
if (Check() == false)
{
return;
}
stoneHelopOb.gameObject.SetActive(false);
//네모 흰색이 비활성화 시킵니다.
}
public void OnPointerClick(PointerEventData eventData)
//클릭시
{
if (Check() == false)
{
return;
}
stoneHelopOb.gameObject.SetActive(false);
//네모흰색 비활성화
var color = transform.GetComponent<Image>().color;
color.a = 1f;
GetComponent<Image>().color = color;
//알파값을 1로 조절해서 보이도록 합니다.
if (omokManager.inst.IsBlackTurn)
{
icon.sprite = omokManager.inst.dollImg[0];
//블랙턴이라면 이미지를 블랙으로
}
else
{
icon.sprite = omokManager.inst.dollImg[1];
//흰턴이라면 이미지를 흰색돌로
}
omokManager.inst.BallClick(row, column);
//해당 좌표를 클릭했다고 해당 매니저에게 보냅니다.
}
public void ballClick(int num)
//num이 1이라면 흑돌이 채워지고 2면 백돌이 3이라면 X표시가 표시됩니다.
{
if (num == 1)
{
var color = transform.GetComponent<Image>().color;
color.a = 1f;
GetComponent<Image>().color = color;
icon.sprite = omokManager.inst.dollImg[0];
}
else if (num == 2)
{
var color = transform.GetComponent<Image>().color;
color.a = 1f;
GetComponent<Image>().color = color;
icon.sprite = omokManager.inst.dollImg[1];
}
else if (num == 3)
{
noOb.SetActive(true);
omokManager.inst.noObs.Add(gameObject);
}
}
public void NoOff()
{
omokManager.inst.ball[column, row] = 0;
noOb.SetActive(false);
}
public void Clear()
//초기화 시킵니다.
{
var color = transform.GetComponent<Image>().color;
color.a = 0f;
GetComponent<Image>().color = color;
omokManager.inst.ball[column, row] = 0;
noOb.SetActive(false);
}
[ContextMenu("Do Something")]
void DoSomething()
//디버그용
{
Transform a = GameObject.Find("Grid").transform;
for (int i = 0; i <= 14; i++)
{
for (int j = 0; j <= 14; j++)
{
GameObject ob = Instantiate(gameObject, a);
ob.name = $"{i},{j}";
}
}
}
[ContextMenu("name")]
void nameset()
//디버그용
{
string[] s = gameObject.name.Split(',');
row = int.Parse(s[1]);
column = int.Parse(s[0]);
}
}
omokManager
using System;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public enum ePlayType
{
None,
BLACK,
WHITE
}
public class omokManager : MonoBehaviour
{
public static omokManager inst;
public Sprite[] dollImg; // 이미지 입니다. 0이 흑돌 이미지,1 흰돌 이미지
public ePlayType myPlayType; // 자신이 흰색인지 흑돌인지
public bool IsPlay = false; // 플레이중
public readonly int SIZE = 15; // 바둑판 사이즈
public bool IsBlackTurn = false; // 턴
public List<GameObject> noObs;
//X표시할 때 담았다가 지울 때 리스트안에 있는 X자를 다 지우면 되니까
public int[,] ball = new int[,]
//좌표 1 흑돌 ,2 흰돌 , 3 X자 표시
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
public GameObject[] ballOb; //돌을 담습니다. 미리 담습니다 15x15 225개
public GameObject ballScan(int row, int column)
//해당 돌 GameObject를 찾는 함수
{
return ballOb[row + (column * SIZE)];
}
private void Awake()
{
inst = this;
}
private void Start()
{
SocketManager.inst.socket.OnUnityThread("GameEnd", data =>
//게임이 끝나면 발생하는 함수
{
String victory = data.GetValue(0).GetString();
String defeat = data.GetValue(1).GetString();
if (GameManager.inst.nickName == victory)
{
GameManager.inst.chatManager.Victory();
GameManager.inst.Warning("승리 했습니다.");
}
else if (GameManager.inst.nickName == defeat)
{
GameManager.inst.chatManager.Defeat();
GameManager.inst.Warning("패배 했습니다.");
}
GameManager.inst.chatManager.gameInfo.text = $"{victory}님께서 승리 했습니다.";
IsPlay = false;
myPlayType = ePlayType.None;
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
GameManager.inst.chatManager.startBtn.SetActive(true);
}
});
SocketManager.inst.socket.OnUnityThread("Turn", data =>
//턴을 넘기는 함수
{
int row = data.GetValue(1).GetInt32();
int column = data.GetValue(2).GetInt32();
if (data.GetValue(0).GetString() == "Black")
{
ball[column, row] = 1;
ballScan(row, column).GetComponent<omokClick>().ballClick(1);
}
else
{
ball[column, row] = 2;
ballScan(row, column).GetComponent<omokClick>().ballClick(2);
}
GameManager.inst.chatManager.gameInfo.text = $"{data.GetValue(3).GetString()}님의 차례입니다.";
TurnChange();
});
SocketManager.inst.socket.OnUnityThread("GameStart", data =>
//게임을 시작합니다.
{
GameStart(data.GetValue(0).GetInt32());
});
}
public void StartBtn()
//게임시작 버튼을 누르면 발생하는 함수
{
if (GameManager.inst.Player1 == GameManager.inst.nickName && GameManager.inst.Player2 != "" && IsPlay == false)
{
GameManager.inst.chatManager.startBtn.SetActive(false);
//버튼을 숨깁니다.
int ran = Random.Range(0, 2);
//0이 나오면 1번이 흑 2번이 백
//1이 나오면 1번이 백 2번이 흑
SocketManager.inst.socket.Emit("GameStart", ran);
}
}
public void StoneSetting()
//중간에 관전자가 들어오면 현재 돌 좌표들을 전부 받아서 셋팅하도록 합니다.
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] == 1 || ball[i, j] == 2)
{
ballScan(j, i).GetComponent<omokClick>().ballClick(ball[i, j]);
}
}
}
}
void GameStart(int num)
//게임 시작시 초기화를 전부 시키고 게임 시작합니다.
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] == 0)
{
continue;
}
ballScan(j, i).GetComponent<omokClick>().Clear();
}
}
IsPlay = true;
IsBlackTurn = true;
if (num == 0)
{
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
myPlayType = ePlayType.BLACK;
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
myPlayType = ePlayType.WHITE;
}
GameManager.inst.chatManager.gameInfo.text = $"{GameManager.inst.Player1}님의 차례입니다.";
}
else
{
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
myPlayType = ePlayType.WHITE;
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
myPlayType = ePlayType.BLACK;
}
GameManager.inst.chatManager.gameInfo.text = $"{GameManager.inst.Player2}님의 차례입니다.";
}
}
public void BallClick(int row, int column)
//돌 클릭 이벤트
{
if (IsBlackTurn)
{
ball[column, row] = 1;
//해당 좌표 설정
}
else
{
ball[column, row] = 2;
//해당 좌표 설정
}
if (VictoryCheck(row, column))
//돌 클릭하고 게임이 끝났는지 파악합니다
{
if (GameManager.inst.Player1 == GameManager.inst.nickName)
{
SocketManager.inst.socket.Emit("GameEnd", GameManager.inst.Player1, GameManager.inst.Player2);
}
else if (GameManager.inst.Player2 == GameManager.inst.nickName)
{
SocketManager.inst.socket.Emit("GameEnd", GameManager.inst.Player2, GameManager.inst.Player1);
}
//첫번째 인자는 승자 두번째는 패자
}
else
{
//게임이 끝난게 아니면 턴을 넘깁니다.
string enemyName = GameManager.inst.Player1 == GameManager.inst.nickName
? GameManager.inst.Player2
: GameManager.inst.Player1;
if (IsBlackTurn)
{
SocketManager.inst.socket.Emit("Turn", "Black", row, column, enemyName);
}
else
{
SocketManager.inst.socket.Emit("Turn", "White", row, column, enemyName);
}
GameManager.inst.chatManager.gameInfo.text = $"{enemyName}님의 차례입니다.";
TurnChange();
}
}
bool VictoryCheck(int row, int column)
{
int ballNum = 1;
//흑은 1번
if (!IsBlackTurn)
{
ballNum = 2;
//흰돌은 2번
}
if (FiveCheck(ballNum))
//5개가 있는지 파악합니다.
{
return true;
}
return false;
}
bool InRange(params int[] v)
//범위가 벗어나면 오류가 뜨기때문에 검사합니다.
{
for (int i = 0; i < v.Length; i++)
if (!(v[i] >= 0 && v[i] < SIZE))
return false;
return true;
}
bool FiveCheck(int ballNum)
{
//완전 탐색합니다.
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] != ballNum)
{
continue;
}
//자신의 돌
//→
//왼쪽으로 돌을 확인합니다.
if (InRange(j + 4) && ball[i, j + 1] == ballNum && ball[i, j + 2] == ballNum &&
ball[i, j + 3] == ballNum && ball[i, j + 4] == ballNum)
{
return true;
}
//↓
//아래칸으로 돌을 확인합니다.
else if (InRange(i + 4) && ball[i + 1, j] == ballNum && ball[i + 2, j] == ballNum &&
ball[i + 3, j] == ballNum && ball[i + 4, j] == ballNum)
{
return true;
}
//↘
//대각선아래로 돌을 확인합니다.
else if (InRange(i + 4, j + 4) && ball[i + 1, j + 1] == ballNum && ball[i + 2, j + 2] == ballNum &&
ball[i + 3, j + 3] == ballNum && ball[i + 4, j + 4] == ballNum)
{
return true;
}
//↙
//왼쪽대각선아래로 돌을 확인합니다.
else if (InRange(i + 4, j - 4) && ball[i + 1, j - 1] == ballNum && ball[i + 2, j - 2] == ballNum &&
ball[i + 3, j - 3] == ballNum && ball[i + 4, j - 4] == ballNum)
{
return true;
}
}
}
return false;
}
public void omokCheck()
{
//렌주룰 이기때문에
//흑만 검사합니다
if (myPlayType != ePlayType.BLACK)
{
return;
}
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
SixCheck(i, j);
}
}
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] != 0)
{
continue;
}
ThreeThreeCheck(i, j);
}
}
}
void SixCheck(int column, int row)
//해당 좌표가 6목이라면 X표시를 합니다.
{
if (ball[column, row] != 0)
{
return;
}
//0번만 확인합니다.
ball[column, row] = 1;
//미리 흑돌을 깔고 6목인지 확인합니다.
//완전탐색을 합니다.
int ballNum = 1;
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (ball[i, j] != 1)
{
continue;
}
//→
if (InRange(j + 5) && ball[i, j + 1] == ballNum && ball[i, j + 2] == ballNum &&
ball[i, j + 3] == ballNum && ball[i, j + 4] == ballNum && ball[i, j + 5] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
//↓
else if (InRange(i + 5) && ball[i + 1, j] == ballNum && ball[i + 2, j] == ballNum &&
ball[i + 3, j] == ballNum && ball[i + 4, j] == ballNum && ball[i + 5, j] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
//↘
else if (InRange(i + 5, j + 5) && ball[i + 1, j + 1] == ballNum && ball[i + 2, j + 2] == ballNum &&
ball[i + 3, j + 3] == ballNum && ball[i + 4, j + 4] == ballNum &&
ball[i + 5, j + 5] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
//↙
else if (InRange(i + 5, j - 5) && ball[i + 1, j - 1] == ballNum && ball[i + 2, j - 2] == ballNum &&
ball[i + 3, j - 3] == ballNum && ball[i + 4, j - 4] == ballNum &&
ball[i + 5, j - 5] == ballNum)
{
//오목이 맞다면 X를 표시합니다.
ball[column, row] = 3;
ballScan(row, column).GetComponent<omokClick>().ballClick(3);
return;
}
}
}
ball[column, row] = 0;
//아무것도 아니라면 다시 0으로 돌림.
}
public void NoClear()
//X표시를 전부 지웁니다.
{
if (myPlayType != ePlayType.BLACK)
{
return;
}
for (int i = 0; i < noObs.Count; i++)
{
if (noObs[i].TryGetComponent(out omokClick click))
{
click.NoOff();
}
}
noObs.Clear();
}
public void TurnChange()
{
IsBlackTurn = !IsBlackTurn;
if (IsBlackTurn)
{
//흑턴
omokCheck();
//착수 금지 표시를 합니다.
}
else
{
//백턴
NoClear();
//본인의 X표시를 전부 지웁니다.
}
}
void ThreeThreeCheck(int i, int j)
{
if (ball[i, j] != 0)
{
return;
}
//다른 돌이 있다면 넘어감
ball[i, j] = 1;
int ThreeValue = 0;
if (FiveCheck(1))
//해당자리가 5오목이라면 착수금지가 사라짐.
{
ball[i, j] = 0;
return;
}
//→
if (ThreeCheck(i, j - 4, i, j - 3, i, j - 2, i, j - 1, i, j + 1, i, j + 2, i, j + 3, i, j + 4)) ++ThreeValue;
//↓
if (ThreeCheck(i - 4, j, i - 3, j, i - 2, j, i - 1, j, i + 1, j, i + 2, j, i + 3, j, i + 4, j)) ++ThreeValue;
//↘
if (ThreeCheck(i - 4, j - 4, i - 3, j - 3, i - 2, j - 2, i - 1, j - 1, i + 1, j + 1, i + 2, j + 2, i + 3, j + 3,
i + 4, j + 4)) ++ThreeValue;
//↙
if (ThreeCheck(i + 4, j - 4, i + 3, j - 3, i + 2, j - 2, i + 1, j - 1, i - 1, j + 1, i - 2, j + 2, i - 3, j + 3,
i - 4, j + 4)) ++ThreeValue;
if (ThreeValue >= 2)
{
//착수금지라면 X표시를합니다.
ball[i, j] = 3;
ballScan(j, i).GetComponent<omokClick>().ballClick(3);
}
else
{
//아니라면 원래대로 돌림
ball[i, j] = 0;
}
}
bool ThreeCheck(int im4, int jm4, int im3, int jm3, int im2, int jm2, int im1, int jm1, int ip1, int jp1, int ip2,
int jp2, int ip3, int jp3, int ip4, int jp4)
{
if (InRange(im4, jm4, ip1, jp1))
if (ball[im4, jm4] == 0 && ball[im3, jm3] == 1 && ball[ip1, jp1] == 0)
{
if (ball[im2, jm2] == 1 && ball[im1, jm1] == 0) return true;
if (ball[im2, jm2] == 0 && ball[im1, jm1] == 1) return true;
}
if (InRange(im3, jm3, ip1, jp1))
if (ball[im3, jm3] == 0 && ball[im2, jm2] == 1 && ball[im1, jm1] == 1 && ball[ip1, jp1] == 0)
return true;
if (InRange(im3, jm3, ip2, jp2))
if (ball[im3, jm3] == 0 && ball[im2, jm2] == 1 && ball[im1, jm1] == 0 && ball[ip1, jp1] == 1 &&
ball[ip2, jp2] == 0)
return true;
if (InRange(im2, jm2, ip2, jp2)) // 중앙
if (ball[im2, jm2] == 0 && ball[im1, jm1] == 1 && ball[ip1, jp1] == 1 && ball[ip2, jp2] == 0)
return true;
if (InRange(im2, jm2, ip3, jp3))
if (ball[im2, jm2] == 0 && ball[im1, jm1] == 1 && ball[ip1, jp1] == 0 && ball[ip2, jp2] == 1 &&
ball[ip3, jp3] == 0)
return true;
if (InRange(im1, jm1, ip3, jp3))
if (ball[im1, jm1] == 0 && ball[ip1, jp1] == 1 && ball[ip2, jp2] == 1 && ball[ip3, jp3] == 0)
return true;
if (InRange(im1, jm1, ip4, jp4))
if (ball[im1, jm1] == 0 && ball[ip3, jp3] == 1 && ball[ip4, jp4] == 0)
{
if (ball[ip1, jp1] == 1 && ball[ip2, jp2] == 0) return true;
if (ball[ip1, jp1] == 0 && ball[ip2, jp2] == 1) return true;
}
return false;
}
}
'유니티 unity' 카테고리의 다른 글
Unity로 node.js WebSocket 통신하기 (socket.io,Mysql 이용해서 붐버맨 만들기)-4 (0) | 2022.07.30 |
---|---|
유니티 Addressable (사용법,서버에서 받기) (0) | 2022.07.26 |
Unity로 node.js WebSocket 통신하기 (socket.io를 이용해서 룸형식 채팅방 구현)-2 (4) | 2022.07.23 |
unity ToolKit 와 UI Builder 이용해서 커스텀에디터만들기(응용편 rpg 아이템에디터)-3 (0) | 2022.07.21 |
unity ToolKit 와 UI Builder 이용해서 커스텀에디터만들기(응용편 소코반 맵 에디터만들기)-2 (0) | 2022.07.21 |