Android 폰으로 USB 테더링하기

|

Android 폰으로 USB 테더링하기

예전에는 별도의 세팅없이 안드로이드 폰으로 USB 테더링이 가능했는데, 어느 순간부터 USB 테더링이 안되도록 변경되었습니다. 그래서 별도의 프로그램을 설치해주어야 USB 테더링을 할 수 있습니다.


프로그램 다운로드

해당 프로그램은 여기에서 다운받을 수 있습니다. 최신 버전으로 다운받도록 합시다. 현재 최신 버전은 여기에서 받을 수 있습니다.


Mac 재부팅

그런 다음, Mac을 재부팅시켜줍니다. 그 이후 안드로이드 폰에서 설정에서 USB 테더링을 켜주면 USB 테더링을 사용할 수 있게 됩니다.

플라이웨이트(Flyweight) 패턴 for Game (C++)

|

Flyweight 패턴

플라이웨이트(Flyweight) 패턴은 객체간 동일한 정보나 유사한 정보들을 같이 공유(Sharing)해서 메모리 사용량을 최소화하는 패턴입니다. 게임에서 나무나 관객 등의 배경을 표현할 때 많이 사용되는 패턴입니다.


나무를 표현하는 클래스

숲의 나무를 표현한다고 할 때, 다음과 같은 데이터들이 필요합니다.

  • 줄기, 잎 등을 표현하는 폴리곤 메시(Mesh)
  • 나무 껍질과 입사귀 텍스처(Texture)
  • 나무의 높이와 굵기
  • 각각의 나무들의 색상을 조금씩 다르게 하기 위한 색의 농도(Tint)

클래스로 표현하면 다음과 같습니다.

class Tree {
 private:
  Mesh mMesh;
  Texture mBark;
  Texture mLeaves;
  Vector mPosition;
  double mHeight;
  double mThickness;
  Color mBarkColor;
  Color mLeavesColor;
};

데이터 종류도 많지만, 메시나 텍스처의 경우 데이터 크기도 큽니다. 이런 나무를 수십, 수백 그루를 표현해야 할 때, 메모리 소모나 성능적인 면에서 한계가 있습니다.


메모리 사용량을 줄이기 위한 방법

특히 메모리를 많이 사용하면서, 공통적인 부분들을 추출하여 하나의 클래스로 만들 수 있습니다. 예를 들어, 다음과 같은 ‘TreeModel’ 클래스를 만들 수 있습니다.

class TreeModel {
 private:
  Mesh mMesh;
  Texture mBark;
  Texture mLeaves;
};

기존 ‘Tree’ 클래스는

class Tree {
 private:
  TreeModel *mTreeModel;

  Vector mPosition;
  double mHeight;
  double mThickness;
  Color mBarkTint;
  Color mLeavesTint;
};

가 됩니다. 나무가 아무리 많더라도 ‘TreeModel’ 클래스는 하나이기 때문에 메모리 사용량은 확실히 줄어듭니다.


지형을 표현하는 방법

조금 예제를 바꾸어서 이번에는 지형을 표현하는 방법입니다. 지형 정보에는 다음과 같은 정보들이 필요합니다.

  • 화면에 렌더링할 때 필요한 텍스처
  • 이동할 때 드는 비용(Cost)
  • 물인지 육지인지

코드로 표현하면 다음과 같습니다.

enum Terrain {
  TERRAIN_GRASS,
  TERRAIN_HILL,
  TERRAIN_RIVER,
};
class World {
 private:
  Terrain mTiles[WIDTH][HEIGHT];
};

int World::getMovementCost(int x, int y) {
  switch (mTiles[x][y]) {
    case TERRAIN_GRASS:
      return 1;
    case TERRAIN_HILL:
      return 2;
    case TERRAIN_RIVER:
      return 3;
  }
}

bool World::isWater(int x, int y) {
  switch (mTiles[x][y]) {
    case TERRAIN_GRASS:
      return false;
    case TERRAIN_HILL:
      return false;
    case TERRAIN_RIVER:
      return true;
  }
}

문제없이 돌아가는 코드이지만 지저분하게 구현되어 있습니다. 데이터와 함수들이 분리되어 있어서 응집도도 떨어지기 때문에 이런 경우는 ‘지형 클래스’를 하나 별도로 구현하는 것이 좋습니다.

class Terrain {
 public:
  Terrain(int movementCost, isWater, Texture texture)
      : mMovementCost(movementCost), mIsWater(isWater), mTexture(texture) {
  }

  int getMovementCost() const {
    return mMovementCost;
  }

  bool isWater() const {
    return mIsWater;
  }

  const Texture &getTexture() const {
    return mTexture;
  }

 private:
  int mMovementCost;
  bool mIsWater;
  Texture mTexture;
};

그리고 ‘World’ 클래스도 각 타일마다 ‘Terrain’ 인스턴스를 하나씩 가지도록 하는 것이 아니라 객체 포인터를 가지게 하여 중복되는 데이터를 공유해서 사용할 수 있도록 하는 편이 바람직합니다.

class World {
 private:
  Terrain *mTiles[WIDTH][HEIGHT];
};

Terrain 인스턴스의 생명 주기를 좀 더 쉽게 관리하기 위해서 ‘World’ 클래스를 다음과 같은 코드로 수정합니다.

class World {
 public:
  World() : mGrassTerrain(1, false, TEXTURE_GRASS), mHillTerrain(2, false, TEXTURE_HILL),
            mRiverTerrain(3, true, TEXURE_RIVER) {}

  const Terrain &getTile() const;

 private:
  void generateTerrain();
  
  Terrain *mTiles[WIDTH][HEIGHT];

  Terrain mGrassTerrain;
  Terrain mHillTerrain;
  Terrain mRiverTerrain;
};

void World::generateTerrain() {
  for (int x = 0; x < WIDTH; x++) {
    for (int y = 0; y < HEIGHT; y++) {
      if (random(10) == 0) {
        mTiles[x][y] = &mHillTerrain;
      } else {
        mTiles[x][y] = &mGrassTerrain;
      }
    }
  }

  int x = random(WIDTH);
  for (int y = 0; y < HEIGHT; y++) {
    mTiles[x][y] = &mRiverTerrain;
  }
}

const Terrain &World::getTile(int x, int y) const {
  return *mTile[x][y];
}

객체 지향 프로그램의 특징

|

객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 특징입니다.


Abstraction

가장 기본 적인 개념입니다. 불필요한 정보는 숨기고 필요한 정보만 일반적인 형태로 표현하는 것을 ‘추상화’라고 합니다.


Inheritance

기존의 클래스의 데이터나 형태 등을 물려받는 파생 클래스를 만들어내는 것을 ‘상속’이라고 하고, 기존 클래스는 부모 클래스, 물려받는 클래스를 자식 클래스라고 합니다. 상속을 통해 기존 클래스의 재활용성을 높이고, 불필요한 코드의 중복을 방지할 수 있습니다. 그리고 객체간의 종속 관계를 형성하여 다형성(Polymorphism)이라는 OOP의 핵심적인 특성을 제공할 수 있게 해줍니다.


Polymorphism

OOP의 핵심입니다. ‘다형성’이라는 말 그대로 특정한 요소가 다양한 형태를 가지게 되는 것을 말합니다.

예를 들면, Animal 이란 클래스가 있다고 가정하면, 이를 상속 받는 Dog 클래스와 Cat 고양이가 있을 수 있습니다. Dog 클래스와 Cat 클래스는 언제든지 Animal 클래스로 대체해서 사용이 가능합니다. (Dog 클래스와 Cat 클래스는 Animal의 모든 속성을 갖고 있기 때문입니다.)

이와 같이 추상화된 클래스를 상속받은 구체화된 클래스는 언제든지 부모 클래스로 취급을 받을 수 있습니다. 이러한 속성은 향후 동적 바인딩(Dynamic Binding)을 가능하게 해줍니다.

메소드 레벨에서의 다형성은 대표적으로 ‘오버로딩(Overloading)’과 ‘오버라이딩(Overriding)’이 있습니다.


Overloading vs Overriding

Overloading은 추가해서 더 싣는다고 생각하면 되며, 파라메터만 바꿔서 같은 이름의 메소드(함수)를 생성할 수 있는 것을 말합니다. 컴파일시에 결정되며 런타임 중에는 변경될 수 없습니다.

Overriding은 아예 태우는 것이라고 생각하면 되며, 상속시 부모 클래스와 자식 클래스의 메소드가 같을 경우 부모 클래스의 메소드 위에 자식 클래스를 태워서 실행시에 자식 클래스의 메소드가 호출되는 것을 말합니다. Dynamic Binding을 위한 중요한 개념입니다. Dynamic Binding은 이름 그대로 런타임 중에 변경될 수 있는 것을 의미합니다.

image -fullwidth

우측 cmd 키로 한/영 전환하기

|

우측 cmd 키로 한/영 전환하기

최신 Mac OS(시에라, Sierra)에서도 통하는 방법입니다. MAC OS에서는 기본적으로 한/영 전환을 Control + Space로 하고 있습니다. 그러다가 OS 버전이 Sierra가 되면서 Caps Lock 키로도 가능하도록 개선되었습니다.

어느 방법이든 Windows에서 일반적으로 사용하던 단축키와 달라 불편함을 겪어서 결국, Mac 에서의 한/영 전환을 Windows에 최대한 가깝게 만들기로 결심했습니다. 그래서 오른쪽 Command 로 한/영 전환을 할 수 있는 방법을 찾아보았습니다.

이 방법은 시에라 및 이전 버전의 OS에서도 작동하는 방법입니다.


Karabiner-Elements 설치

먼저 Karabiner-Elements를 설치합니다. 간편하게 여기에서 설치 파일을 다운받을 수 있습니다.


단축키 매핑

그런 다음 Karabiner-Elements를 실행합니다. 그리고 다음 그림과 같이 오른쪽 Command 키에 특수한 키를 매핑시키도록 합시다.

image

맥북 키보드엔 F1 ~ F12 까지 밖에 없으니, 그 이후 버튼(F13 ~ F20 등)을 매핑시켜주면 됩니다.

그런 다음 시스템 환경 설정으로 가서 키보드 설정을 선택합니다.

image

그리고는 입력 소스 단축키를 다음과 같이 설정합니다.

image

커맨드(Command) 패턴 for Game (C++)

|

Command 패턴

커맨드(Command) 패턴은 메소드 호출을 객체로 감싼 패턴입니다.

커맨드 패턴을 적용하지 않고 일반적으로 구현한 경우를 살펴보겠습니다.

게임에서 조이패드의 버튼이 X, Y, A, B의 4개가 있다고 하고, 각 버튼을 눌렀을 때의 행동을 처리하는 handleInput() 메소드가 있다면 다음과 같이 구현될 수 있습니다.


InputHandler

void InputHandler::handleInput() {
  if (isPressed(BUTTON_X)) jump();
  else if (isPressed(BUTTON_Y)) attack();
  else if (isPressed(BUTTON_A)) changeWeapon();
  else if (isPressed(BUTTON_B)) dash();
}

단순하게 위와 같은 코드로 구현을 해도 되지만, 많은 게임들이 입력 키의 맵핑을 변경할 수 있는 기능을 제공하고 있습니다.


Command

키 변경 지원을 위해서는 각 명령을 버튼에 직접 할당하는 것이 아닌, 특별한 객체를 이용할 필요가 있습니다. 따라서 다음과 같은 Command 추상 클래스를 선언할 수 있습니다.

class Command {
 public:
  virtual ~Command() {}
  virtual void execute() = 0;
};

이제 각 행동별로 하위 클래스를 만들면 됩니다.

class JumpCommand: public Command {
 public:
  virtual void execute() { jump(); }
};


InputHandler 수정

InputHandler 클래스는 다음과 같이 수정될 것입니다.

class InputHandler {
 public:
  void handleInput();

 private:
  Command *pButtonX;
  Command *pButtonY;
  Command *pButtonA;
  Command *pButtonB;
};

또한 구현부의 handleInput() 메소드도 다음과 같이 변경될 수 있습니다.

void InputHandler::handleInput() {
  if (isPressed(BUTTON_X)) pButtonX->execute();
  else if (isPressed(BUTTON_Y)) pButtonY->execute();
  else if (isPressed(BUTTON_A)) pButtonA->execute();
  else if (isPressed(BUTTON_B)) pButtonB->execute();
}

여기까지가 커맨드 패턴의 핵심 부분입니다.


Actor와 Command 분리하기

위의 코드와 같이 커맨드 패턴을 이용하여 각 키들의 맵핑과 행동간의 분리를 할 수 있었습니다. 하지만, Actor와 Command 간에는 여전히 커플링(Coupling)이 존재합니다. jump(), attack() 등의 행동은 플레이어 객체만 할 수 있습니다.

Actor와 Command를 분리하기 위해서 Command 클래스를 다음과 같이 변경합니다.

class Command {
 public:
  virtual ~Command() {}
  virtual void execute(GameActor &actor) = 0;
};

JumpCommand 클래스는 다음과 같이 변경됩니다.

class JumpCommand: public Command {
 public:
  virtual void execute(GameActor &actor) { actor.jump(); }
};

이제 JumpCommand 클래스는 어떤 캐릭터에게도 적용할 수 있게 되었습니다. handleInput() 메소드는 다음과 같이 변경하여 Command 객체를 리턴하도록 합니다.

Command *InputHandler::handleInput() {
  if (isPressed(BUTTON_X)) pButtonX;
  else if (isPressed(BUTTON_Y)) pButtonY;
  else if (isPressed(BUTTON_A)) pButtonA;
  else if (isPressed(BUTTON_B)) pButtonB;

  return nullptr;
}

이제 GameActor 클래스에는 다음 코드가 필요합니다.

  Command* command = inputHandler.handleInput();
  if(command) {
    command->execute(actor);
  }

이제 명령을 실행할 때 Actor만 변경하면 어떤 Actor라도 제어가 가능하게 되었습니다.


Undo & Redo

Undo 기능과 Redo 기능은 Command 패턴으로 쉽게 구현할 수 있는 대표적인 기능들입니다.

Command들을 리스트로 관리하는 CommandStack을 이용해서 구현할 수도 있고, Command 클래스 자체가 이전 상태를 관리하도록 하고 그 안에 undo() 함수를 넣어서 구현할 수도 있습니다.

CommandStack을 이용하는 방법은 여기에 있습니다.

여기서는 Command 클래스 자체에 이전 상태를 저장하고, undo() 함수도 가지도록 하겠습니다.

class Command {
 public:
  virtual ~Command() {}
  virtual void execute() = 0;
  virtual void undo() = 0;
};
class MoveUnitCommand: public Command {
 public:
  MoveUnitCommand(Unit *unit, int x, int y) : pUnit(unit) {
    prevX(0);
    prevY(0);
    curX(x);
    curY(y);
  }

  virtual void execute() {
    prevX = pUnit->x();
    prevY = pUnit->y();
    pUnit->moveTo(curX, curY);
  }

  virtual void undo() {
    pUnit->moveTo(prevX, prevY);
  }

 private:
  Unit *pUnit;
  int curX, curY;
  int prevX, prevY;
};

하지만, 결국 Undo 기능과 Redo 기능을 제대로 구현하려면 Stack이 필요하긴 합니다. 그래야 현재의 Command 포인터를 효율적으로 관리할 수 있습니다.