Course 4 : 좀비 전투 시스템

학미새🐥·2022년 8월 28일
0

1. Payable

🤍함수 제어자 복습🤍

👆🏻 접근 제어자
함수가 언제, 어디서 호출될 수 있는지를 제어!

  • private : 컨트랙트 내부의 다른 함수들에서만 호출 가능
  • internal : private과 유사하지만, 컨트랙트를 상속하는 컨트랙트에서도 호출될 수 있음
  • external : 오직 컨트랙트 외부에서만 호출 가능
  • public : 내외부 모두에서 어디서든 호출 가능

✌🏻 상태 제어자
블록체인과 상호작용하는 방법!

  • view : 어떤 데이터도 저장/변경되지 않는 함수
  • pure : 어떤 데이터도 블록체인에 저장하지 않고, 읽지도 않는 함수
    -> 둘다 컨트랙트 외부에서 호출될 시 가스를 소모하지 않는다!

🤟🏻 사용자 정의 제어자
제어자들이 함수에게 어떻게 영향을 줄지 개발자가 직접 로직을 정의할 수 있다

+) 제어자는 하나의 함수에 여러개 사용될 수 있다!

새로운 제어자, "payable"!

  • 함수 실행과 동시에 컨트랙트에 돈을 지불 할 수 있는 함수 유형
  • payable 함수 예시
  function buySomething() external payable {
    // 함수 실행에 0.001이더가 보내졌는지 확실히 하기 위해 확인:
    require(msg.value == 0.001 ether);
    // 보내졌다면, 함수를 호출한 자에게 디지털 아이템을 전달하기 위한 내용 구성:
    transferThing(msg.sender);
  }
  • msg.value : 컨트랙트로 보내진 ETH 값

  • ether : ETH의 기본 단위

  • 위의 함수를 web.js(Dapp프론트)에서 호출하는 예시

컨트랙트명.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})
  • value 필드 : 함수 호출하며 몇 ether를 보낼지 결정. (ex-0.001)
  • 위처럼 ether를 보내려고 시도할 때, 호출된 함수가 payable이 아니라면! 트랜잭션은 거부당함.

2. 출금

  • 유저가 payable 함수를 통해 컨트랙트로 보낸 이더리움은 컨트랙트의 이더리움 계좌에 갇히게 된다.
  • 이를 나의 지갑으로 옮기려면 이더 인출 함수를 컨트랙트에 만들어야 한다.
contract GetPaid is Ownable {
  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }
}
  • Ownable을 상속받음으로써
    • owneronlyOwner제어자를 사용할 수 있음
    • transfer 함수 : 이더를 특정 주소로 전달
  • this.balance : 컨트랙트에 저장되어있는 전체 잔액 반환

    🔽 transfer 함수 사용법

    이더수신자주소.transfer(보낼 이더 값);

3. 좀비 전투

새로운 컨트랙트 만드는 법

  1. ~.sol 파일 생성
  2. pragma solidity ^0.4.19; : 솔리디티 버전 설정
  3. import "./컨트랙트명.sol" : 상속할 컨트랙트 파일 import하기
  4. contract 컨트랙트명 is 상속컨트랙트명 : 컨트랙트 정의 시작

4. 난수(Random Numbers)

keccak256을 통한 난수 생성

  • keccak256 해시함수를 사용해 난수를 생성해보자.
// 1~100 난수 생성
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
  • 그러나 이 방법은 정직하지 않은 노드의 공격에 취약하다
  • 안전한 방법은, 블록체인 이더리움의 외부에서 oracle을 통해 데이터를 받아오는 방법이 있다.

5. 좀비 싸움

🤍 좀비 싸움 로직

  • 나의 좀비 중 하나를 고르고, 상대 좀비를 공격 대상으로 선택
    8. 공격으로 돌아가자!
  • 공격 좀비는 승률 70%, 방어 좀비는 승률 30%
    uint attackVictoryProbability = 70;
  • 모든 좀비는 전투 결과에 따라 증가하는 winCount, lossCount 가짐
    9. 좀비 승리와 패배
  • 공격 좀비가 이기면, 좀비 레벨 상승 & 새 좀비 생성
    10. 좀비 승리
  • 좀비가 지면, 아무일도 일어나지 않음 (lossCount만 증가)
    11. 좀비 패배
  • 이기든 지든, 공격 좀비의 재사용 대기시간 활성화
    _triggerCooldown(myZombie);

6. 공통 로직 구조 개선(Refactoring)

반복적으로 사용되는 require문이 있다면, modifier로 분리시키자.

7. 구조 더 개선하기

(새로 배운 내용 없음)

8. 공격으로 돌아가자!

  • 나의 좀비 중 하나를 고르고, 상대 좀비를 공격 대상으로 선택
    Zombie storage myZombie = zombies[_zombieId];
    Zombie storage enemyZombie = zombies[_targetId];
  • 구조체의 포인터를 storage로 얻어서 사용하기 쉽게 만든다.

9. 좀비 승리와 패배

  • 모든 좀비는 전투 결과에 따라 증가하는 winCount, lossCount 가짐
    struct Zombie {
      string name;
      uint dna;
      uint32 level;
      uint32 readyTime;
      // 1. 여기에 새로운 속성을 추가하게
      uint16 winCount;
      uint16 lossCount;
    }

10. 좀비 승리

  • 공격 좀비가 이기면, 좀비 레벨 상승 & 새 좀비 생성
    if (rand <= attackVictoryProbability) {
      myZombie.winCount++;
      myZombie.level++;
      enemyZombie.lossCount++;
      feedAndMultiply(_zombieId,enemyZombie.dna, "zombie");
    }

11. 좀비 패배

  • 좀비가 지면, 아무일도 일어나지 않음 (lossCount만 증가)
else {
      myZombie.lossCount++;
      enemyZombie.winCount++;
    }
profile
뭐든 다해보려는 공대생입니다

0개의 댓글