1. 程式人生 > >eth實戰專案遊戲開發 TICTACTOE 二

eth實戰專案遊戲開發 TICTACTOE 二

eth dapp,前端部分,使用使用truffle 框架,前端部分使用react,eht互動庫truffle-contract

合約部分請點選 https://blog.csdn.net/bondsui/article/details/85755186

github程式碼在文末

文章目錄

3 小白如何寫前端

github,全球最大的程式碼庫,你想要的基本都有。

3.1 github搜尋

TicTacToe,本例中使用react,直接搜尋 TicTacToe react,按照starts排序

image-20190103234247776

3.2 clone buile 專案

➜  temp git clone 
[email protected]
:trihargianto/reactjs-tictactoe.git ➜ temp cd reactjs-tictactoe ➜ npm install ➜ npm start

3.3 檢視效果和程式碼

效果圖,自帶動畫,重點檢視點選事件

image-20190103234703062

render() {
        const indexSquares = [0,1,2,3,4,5,6,7,8];

        var squares = indexSquares.map(function(indexSquare, i) {
            return
(<Square value={this.state.squares[i]} key={i} index={i} winner={this.state.winner} xIsNext={this.state.xIsNext} onClick={this.handleOnClick.bind(this)} />) },this) return ( <div> <h1 style={{textAlign: 'center', fontSize: '46px', color: 'rgba(52, 152, 219,1.0)'}} className="animated flipInY">Tic-Tac-Toe</h1> <h3 style={{textAlign: 'center'}} id="titlePemenang">{this.state.winner !== null ? <span>Pemenangnya <b>{this.state.winner}</b></span> : ""}</h3> <div className="container animated fadeInUp"> <div className="row"> <br /> <div className="col-xs-12">{squares}</div> </div> </div> <br /> {this.state.winner !== null || this.state.full === true ? <ResetButton onClick={this.handleResetGame.bind(this)} /> : ""} </div> ) } }

image-20190103235024205

4 前端頁面編寫

4.1 頁面樣式

拷貝下載下來的git專案(別忘了css檔案一同拷貝),安裝包

"bootstrap": "^3.3.7",
"jquery": "^3.2.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",

執行專案,檢視頁面

4.2 調整頁面

通過程式碼,得知,原頁面的棋盤的值為0-8,我們改為[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]],每個value即棋盤的座標。同時更改Square的handleClick,把值傳遞到頁面。

執行專案,點選第一個模組,顯示log 0,1

剩下的就是與合約互動。

class Board extends React.Component {

    constructor() {
        super();

        this.state = {
            squares : Array(9).fill(null),
            xIsNext : true,
            winner  : null,
            full    : false
        }
    }

    handleOnClick(index, turn) {
        console.log(index,turn)
    }
   
    handleResetGame() {
        window.location.reload()
    }

    render() {
        const indexSquares = [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]];

        var squares = indexSquares.map(function(indexSquare, i) {
            return (<Square
                value={this.state.squares[i]}
                key={i}
                index={i}
                winner={this.state.winner}
                xIsNext={this.state.xIsNext}
                onClick={this.handleOnClick.bind(this)} />)
        },this)

        return (
            <div>
                <h1 style={{textAlign: 'center', fontSize: '46px', color: 'rgba(52, 152, 219,1.0)'}} className="animated flipInY">Tic-Tac-Toe</h1>
                <h3 style={{textAlign: 'center'}} id="titlePemenang">{this.state.winner !== null ? <span>Pemenangnya <b>{this.state.winner}</b></span> : ""}</h3>
                <div className="container animated fadeInUp">
                    <div className="row">
                        <br />
                        <div className="col-xs-12">{squares}</div>
                    </div>
                </div>
                <br />
                {this.state.winner !== null || this.state.full === true ? <ResetButton onClick={this.handleResetGame.bind(this)} /> : ""}
            </div>
        )
    }
}

4.3 下載專案引用包

"truffle-contract": "^3.0.7",
"web3": "^1.0.0-beta.37",
"xmlhttprequest": "^1.8.0"

4.4 搭建框架

補充頁面按鈕,更新square

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import './animate.css'
import Square from './Square'
import MenuButtons from './MenuButtons'
import getWeb3 from "./utils/getWeb3"
import contract from 'truffle-contract'
import _TicTacToe from './contracts/TicTacToe.json'


// 棋盤值
const STONES = [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]];
const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000"
const GAME_COST = "1"; //1 eth
let web3 = null

class Board extends React.Component {

    state = {
        accounts: null, // 賬戶

        // 棋盤相關
        instance: null, // 遊戲例項
        board: [['', '', ''], ['', '', ''], ['', '', '']],
        player1: '', // 玩家一,等於建立者
        player2: '', // 玩家二,
        nextPlayer: '',// 該誰下棋

        winner: '',
        gameResult: '', // 遊戲結果,
    }

    // 獲取合約
    getTicTacToe = () => {
        let TicTacToe = contract(_TicTacToe)
        TicTacToe.setProvider(web3.currentProvider);
        return TicTacToe
    }

    componentDidMount = async () => {
        try {
            web3 = await getWeb3();
            console.log("currentProvider", web3.currentProvider)
            const accounts = await web3.eth.getAccounts();
            console.log("accounts", accounts)
            this.setState({accounts});
        } catch (error) {
            alert(`web3 載入失敗`,);
            console.error(error);
        }
    }

    componentWillUnmount() {
        this.unRegistNextPlayerEvent()
    }

    // 點選建立遊戲
    onCreateGameClick = () => {
        console.log("建立遊戲")
    }

    // 加入遊戲
    onJoinGameClick = async () => {
        console.log("加入遊戲")
    }

    // 更新遊戲面板
    updateBoard = () => {
        console.log("更新面板")
    }

    // 設定棋盤
    onStoneClick = (stone) => {
        console.log("設定棋盤",stone)
    }

    // 重置遊戲
    onResetGameClick = () => {
        window.location.reload()
    }

    // 設定贏家
    setWinner = (winner) => {
        let gameResult = ""
        if (winner === this.state.player1) {
            gameResult = "恭喜勝利,再來一局"
        } else if (winner === this.state.player2) {
            gameResult = "失敗,再接再厲"
        } else {
            gameResult = "旗鼓相當,平局,再來一局"
        }
        this.setState({gameResult})
    }

    // 00 01 02
    // 10 11 12
    // 20 21 22
    render() {
        let {accounts, board, player1, player2, nextPlayer, instance, winner} = this.state
        let gameAddress = instance == null ? "" : instance.address
        console.log("state", this.state)
        console.log("", instance)
        if (winner != ''){
            winner = winner == EMPTY_ADDRESS ? "平局,退回賭金" : "贏家為:"+winner
        }
        let squares = STONES.map((stone, i) => {
            return (<Square
                key={i}
                stone={stone}
                board={board}
                account={accounts == null ? null : accounts[0]}
                nextPlayer={nextPlayer}
                onClick={this.onStoneClick}/>)
        })

        return (
            <div style={{textAlign: 'center'}}>
                {/*標題*/}
                <h2 style={{fontSize: '42px', color: "red"}}
                    className="animated flipInY">性感荷官 線上發牌
                </h2>
                {/* 遊戲資訊展示*/}
                <div>
                    <h5>當前使用者:{accounts == null ? "未檢測到" : accounts[0]}</h5>
                    <h5>遊戲地址:{gameAddress == "" ? "等待建立" : gameAddress}</h5>
                    <h5>player1:{player1 == "" ? "等待加入" : player1}</h5>
                    <h5>player2:{player2 == "" ? "等待加入" : player2}</h5>
                    <h5>nextPlayer:{player2 == "" ? "遊戲未開始" :
                        <span style={{color: "red"}}>{nextPlayer}</span>}</h5>
                    <h3 id="gameResult">
                        <span style={{fontSize: '32px', color: "red"}}>
                            <b>{winner}</b></span>
                    </h3>
                </div>
                {/*棋盤*/}
                <div className="container animated fadeInUp">
                    <div className="row">
                        <div className="col-xs-12">{squares}</div>
                    </div>
                </div>
                <br/>

                <MenuButtons
                    onCreateGameClick={this.onCreateGameClick}
                    onJoinGameClick={this.onJoinGameClick}
                    onResetGameClick={this.onResetGameClick}/>
            </div>
        )
    }
}

ReactDOM.render(
    <Board/>,
    document.getElementById('root')
);

Square.js

es6語法普及,封包與解包

import React from 'react';

const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000"
export default class Square extends React.Component {

    constructor(props) {
        super(props)
        // es6 語法
        // 本頁面要設定狀態,使用者點選,提前顯示該位置內容,避免等待,如不需要否則可直接使用props或者無狀態元件
        // 儲存所有屬性到狀態變數
        this.state = {...props}

        // 等價
        // let {key,stone,board,account,nextPlayer,onClick}=props
        // this.state = {key,stone,board,account,nextPlayer,onClick}
        console.log(this.state)
    }


    componentWillReceiveProps(nextProps) {
        this.state = {...nextProps}
    }

    // 狀態變數未Object,可以不寫預設值,
    state = {}
    
    // 點選事件
    onItemClick(event) {
        let errMsg = this.mustCheck()
        if (errMsg != "") {
            event.target.className += " animated shake";
            alert(errMsg)
            return
        }
        // 引用點選
        this.state.onClick(this.state.stone);
        // 設定 修改
        event.target.className += " animated rubberBand square-container-active";
    }

    // 下棋校驗,返回錯誤資訊,沒有返回空
    mustCheck() {
        let {account, nextPlayer} = this.state
        console.log(this.state)
        if (this.state.account == null) {
            return "使用者未登入"
        }

        if (nextPlayer == '') {
            return "遊戲未開始"
        }

        if (account.toLowerCase() != nextPlayer.toLowerCase()) {
            return "等待對方下子"