'유니티'에 해당되는 글 227건

  1. 2014.04.10 유니티 event delegate 예제 (1)
  2. 2014.04.10 유니티 퀘스트 관련
  3. 2014.04.09 유니티 스크립트 호출 순서 제어
  4. 2014.04.08 PlayerPrefs 배열로 저장하기.
  5. 2014.04.08 NGUI 스크롤뷰 grid 아이템 생성시 순서대로 생성되게 하기.
  6. 2014.04.07 유니티 인벤토리 관련 구현
  7. 2014.04.07 delegate, action, event 활용 관련
  8. 2014.04.02 유니티 안드로이드 확장 개발 - jar 파일(이클립스연동) (3)
  9. 2014.03.28 인앱빌링 구현하기 ver3 - 구글번역
  10. 2014.03.27 안드로이드 서버 php 통신(간단히) (11)

유니티 event delegate 예제

|


버튼을 누르면 해당 오브젝트 삭제


Trackback 0 And Comment 1
  1. BlogIcon 2014.04.14 11:12 address edit & del reply

    대다나다

유니티 퀘스트 관련

|
보통 퀘스트 메니저를 두고 서버 접속시 받아온 퀘스트 정보로 초기화후 모든 행동의 코드 조각들에서 퀘스트 메니저로 이벤트를 날립니다( 이벤트 구현 방식은 하기 나름인데 보통 c#을 사용중이시면 대리자를 이용 합니다 ) 이 때 현재 어떤 행동인지와 값(보통 특정아이템 x개 수집 같은퀘스트의 경우 값이 필요 할수도 있습니다.)을 나타내는 명세를 이벤트에 같이 실어 보내고 퀘스트 메니저에선 이 이벤트의 명세를 보고 해석 하여 퀘스트의 해결 여부를 판단하였다가 서버에 알리는 식으로 하는게 보통 쓰는 방식 입니다. 행동의 종류가 많을 수록 코드 관리가 어려워 지는데( switch case 도배.. ) 보통은 이쯤 되면 상태 기계화 시켜 또 세분하 하여 관리 하면 됩니다. 스테이트 패턴을 적용해도 좋구요..



이벤트 사용법













스테이트패턴




'유니티 > Note' 카테고리의 다른 글

하이브리드 app - 2일차  (0) 2014.04.15
유니티 샘플예제 penelope  (0) 2014.04.14
유니티 퀘스트 관련  (0) 2014.04.10
유니티 스크립트 호출 순서 제어  (0) 2014.04.09
유니티 인벤토리 관련 구현  (0) 2014.04.07
delegate, action, event 활용 관련  (0) 2014.04.07
Trackback 0 And Comment 0

유니티 스크립트 호출 순서 제어

|

Edit - Project Setting - Script Excution Order 눌러서 스크립트 호출 순서제어가 가능합니다.




Trackback 0 And Comment 0

PlayerPrefs 배열로 저장하기.

|

데이터가 int[] _data 안에 들어 있다고 가정하겠습니다. 
string _tmpStr = ""; 

for (int i = 0; i < _data.Length;i ++) { 
  _tmpStr = _tmpStr + _data[i]; 
  if (i < _data.Length) { 
      _tmpStr = _tmpStr + ","; 
  } 


PlayerPrefs.SetString("Data", _tmpStr);로 보존 합니다. 
불러 올때는 string[] _dataArr = PlayerPrefs.GetString("Data").Split(','); 하신후 
int[] _dataIn = new int[_dataArr.Length]; 
for (int i = 0; i < _dataArr.Length; i++) { 
  _dataIn[i] = System.Convert.ToInt32(_dataArr[i]); 

하시면 될 듯 합니다.

Trackback 0 And Comment 0

NGUI 스크롤뷰 grid 아이템 생성시 순서대로 생성되게 하기.

|

NGUI 스크롤 뷰에서 아이템이 생성될때 먼저 생성된 아이템이 맨 밑으로 정렬됨.





UIGrid 에서 sorted 를 누르면 먼저 생성된 아이템이 맨 위로 생성되는 것을 볼수 있다.

다른 옵션들도 많을듯.


Trackback 0 And Comment 0

유니티 인벤토리 관련 구현

|








자료 구조를 말씀하시는 건가요? 
보통 사이즈가 정해져 있는 인벤 같은 경우에는 배열로 하고, 
유동적으로 늘어나는 것은 맵 같은 자료를 많이 써요. 
아무래도 인벤토리의 아이템들은 서치도 빈번하게 일어나니까요. 
또 인덱스 위치랑 바로 매칭이 되고요. 
저도 기존에 온라인 rpg 유지보수 일 하면서 소소를 많이 봤는데 대부분 이런식으로 하더라구요. 
그리고 이번에 직접 플젝에 인벤을 구현했는데 직접 손으로 만들어보니까 생각보다 
신경 써야 할 부분들이 많더라구요. 
인벤토리 한 칸에 들어가야 할 정보들... 즉, 아이템 하나가 가지는 속성들을 잘 생각하셔서 
구조체로 만들고 이것들을 관리하는 자료구조를 잘 적용하고 설계하는 것을 주 목표로 잡으시면 될 것 같네요







일단 첨부하신건 보지 않았습니다. 

아이템에 수량이 존재한다면 다음처럼 풀 수 있을 것입니다. 

저는 c#으로 짜기때문에 c#으로 작성해보겠습니다. 

갯수를 가진 아이템 넣고 빼는 것중에서 가장 심플한 형태라고 생각합니다. 




이 재료로 이제 알아서 뿌려주시면 됩니다. 

UI와 연결하는것이나 아이템을 저장&불러오는 것은 본인의 재량으로... 

저 컴포넌트를 가진 게임오브젝트를 인벤토리로 만드시고 

OnGUI 함수 만드셔서 직접 뿌려줘도 될 것입니다.













Stacking Items in a Inventory List

0
1

I have my inventory script, an enemy script with item dropping and looting, and an Item Class script all in Java. It all works fine, but I'm trying to find a way to make Items stackable. I have added a stackable boolean to my items and set them to true as well as a stack limit and amount variable. What I don't understand is how, when a button is clicked, can I search through a list for that item, if it exists change a varialble on that item. That is my only problem with this script right now. Here are those scripts(one being just a snippet).

My Item Class.

  1. class ItemClass{
  2. public var ItemID : int;
  3. public var Name : String;
  4. public var icon : Texture2D;
  5. public var Description : String;
  6. public var Value : int;
  7. var Amount : int;
  8. public var itemType : ItemType;
  9. public var potionType : PotionType;
  10. public var potionAmount : int;
  11. public var stackAble : boolean;
  12. public var maxStack : int;
  13.  
  14. public enum ItemType{
  15. Potion,
  16. Weapon,
  17. QuestItem
  18. };
  19.  
  20.  
  21. public enum PotionType{
  22. None,
  23. Heal,
  24. Stamplus,
  25. strplus,
  26. agplus
  27. };
  28. }

My Enemy

  1. import System.Collections.Generic;
  2.  
  3. var life = 100;
  4. var exp = 50;
  5. var drops : List.<ItemClass> = new List.<ItemClass>();
  6. var looting : boolean = false;
  7. var lootable : boolean = false;
  8. var lootRect : Rect = Rect(500,500,250,75);
  9. var LOOT_ID : int = 3;
  10.  
  11. var skin : GUISkin;
  12.  
  13.  
  14. function Start () {
  15.  
  16.  
  17. }
  18.  
  19. function Update () {
  20.  
  21.  
  22. if (life <= 0 && lootable == false ){
  23. gameObject.SendMessageUpwards("AddExp", exp);
  24. gameObject.tag = "Lootable";
  25. lootable = true;
  26. }
  27. }
  28.  
  29. function OnGUI(){
  30. GUI.skin = skin;
  31.  
  32. var levelManager = GameObject.Find("LevelManager").GetComponent(LevelManager);
  33.  
  34. if(looting == true && levelManager.paused == true ){
  35. lootRect = GUI.Window(LOOT_ID,lootRect,LootWindow,"Looting" + this.name);
  36. lootRect.y = Mathf.Clamp(lootRect.y,0,Screen.height-lootRect.height);
  37. lootRect.x = Mathf.Clamp(lootRect.x,0,Screen.width-lootRect.width);
  38. }
  39. if(looting == true && levelManager.paused == false){
  40. looting = false;
  41. }
  42. }
  43.  
  44. function Hit(){
  45. life -= 10;
  46.  
  47. }
  48.  
  49.  
  50.  
  51. function LootWindow(id : int){
  52. GUI.BringWindowToFront(id);
  53. GUI.DragWindow(Rect(20,0,lootRect.width-20 ,15));
  54. for( var x : int = 0; x< drops.Count; x++){
  55. if(GUI.Button(Rect(10 +(x * 45), 20,45,45), GUIContent(drops[x].icon,drops[x].Description), skin.customStyles[1])){
  56. if(drops[x].stackAble.Equals(true) && PlayerInventory.Inventory.Contains(drops[x])){
  57. var item = PlayerInventory.Inventory[PlayerInventory.Inventory.IndexOf(drops[x])];
  58. item.Amount += 1;
  59. drops.RemoveAt(x);
  60. }
  61. else
  62. {
  63. PlayerInventory.Inventory.Add(drops[x]);
  64. drops.RemoveAt(x);
  65. }
  66. }
  67. }
  68. if (GUI.Button(Rect(3,3,10,10),"x")){
  69. looting = false;
  70. }
  71.  
  72. }
  73.  
  74.  
  75.  
  76.  
  77. function Loot(){
  78. looting = true;
  79. return;
  80. }

my inventory class

  1. #pragma strict
  2. import System.Collections.Generic;
  3.  
  4.  
  5. public class PlayerInventory
  6. {
  7.  
  8. static var Inventory : List.<ItemClass> = new List.<ItemClass>();
  9.  
  10.  
  11. }
  12.  
  13.  
  14.  
  15. function Start(){
  16.  
  17. }
  18. function Update(){
  19.  
  20.  
  21. }

And the snippet that deals with the inventory in my levelmanager script.

  1. function Inventory(id : int){
  2.  
  3. GUI.DragWindow(Rect(0,0,InventoryRect.width,15));
  4.  
  5. var cnt : int = 0;
  6.  
  7.  
  8.  
  9. for(var x = 0; x < inventoryRows; x++){
  10. for(var y = 0; y < inventoryCols; y++){
  11. if (cnt < PlayerInventory.Inventory.Count){
  12.  
  13. var texture : Texture2D = PlayerInventory.Inventory[cnt].icon;
  14. var description : String = PlayerInventory.Inventory[cnt].Name + "\n" + PlayerInventory.Inventory[cnt].Description;
  15. if(GUI.Button(Rect(10 + (y * itemWidth ),20 + (x * itemHeight ), itemWidth, itemHeight),GUIContent(texture,description), customSkin.customStyles[1])){
  16. if(PlayerInventory.Inventory[cnt].itemType.Equals(ItemClass.ItemType.Potion) && PlayerInventory.Inventory[cnt].potionType.Equals(ItemClass.PotionType.Heal) && PlayerInventory.Inventory[cnt].Amount > 1){
  17. Health += PlayerInventory.Inventory[cnt].Amount;
  18. PlayerInventory.Inventory[cnt].Amount-= 1;
  19. if(PlayerInventory.Inventory[cnt].Amount == 1){
  20. PlayerInventory.Inventory.RemoveAt(cnt);
  21. }
  22. }
  23. else if(PlayerInventory.Inventory[cnt].itemType.Equals(ItemClass.ItemType.Weapon)){
  24. if(!GetComponent(WeaponManager).WeaponSlot[0].Name.Equals(""))
  25. PlayerInventory.Inventory.Add(GetComponent(WeaponManager).WeaponSlot[0]);
  26. GetComponent(WeaponManager).WeaponSlot[0] =(PlayerInventory.Inventory[cnt]);
  27. PlayerInventory.Inventory.RemoveAt(cnt);
  28.  
  29. }
  30. else{
  31. PlayerInventory.Inventory.RemoveAt(cnt);
  32. }
  33. }
  34. }
  35. else{
  36. GUI.Box(Rect(10 + (y * itemWidth ),20 + (x * itemHeight ), itemWidth, itemHeight),"", customSkin.customStyles[0]);
  37. }
  38. cnt++;
  39.  
  40. }
  41. }
  42. }

I'm not looking for a system all coded out, or I would have already done that. I like the way my system works, and the fact I made it. this is the first time I've come to the answers community and I hope I can learn something here. Any help would be appreciated :)

more ▼

asked Aug 17 '12 at 09:32 PM

mactinite77 gravatar image

mactinite77 
58  1  1  3


2 answers:sort voted first 
1

Well there are some things i would change ;)

First the boolean is redundant. Since you have a MaxStack variable you can simply set it to 0 or -1 for items that aren't stackable.

Next thing is how can you tell that two items are of the same type? ItemID? Name? You have to specify a criteria which you can use to identify similar items.

I would probably create a filter function. Something like that:

  1. public class PlayerInventory
  2. {
  3. static var Inventory : List.<ItemClass> = new List.<ItemClass>();
  4. static function FilterItems(ID : int) : List.<ItemClass>
  5. {
  6. var result = new List.<ItemClass>();
  7. for(var item : ItemClass in Inventory)
  8. {
  9. if (item.ItemID == ID && item.Amount < item.maxStack)
  10. result.Add(item);
  11. }
  12. return result;
  13. }
  14.  
  15. }
  16.  
  17. // [...]
  18. for( var x : int = 0; x< drops.Count; x++)
  19. {
  20. if(GUI.Button(Rect(10 +(x * 45), 20,45,45), GUIContent(drops[x].icon,drops[x].Description), skin.customStyles[1]))
  21. {
  22. if( drops[x].maxStack > 0)
  23. {
  24. var stacks = PlayerInventory.FilterItems(drops[x].ItemID);
  25. if (stacks.Count > 0)
  26. {
  27. stacks[0].Amount++;
  28. drops.RemoveAt(x);
  29. x--; // Since we removed this one
  30. }
  31. else // when there is no stack yet or all stacks are full, add a new one
  32. {
  33. PlayerInventory.Inventory.Add(drops[x]);
  34. drops.RemoveAt(x);
  35. x--;
  36. }
  37. }
  38. else
  39. {
  40. PlayerInventory.Inventory.Add(drops[x]);
  41. drops.RemoveAt(x);
  42. x--;
  43. }
  44. }
  45. }

Note: I just wrote / modified that here on UA so there might be some syntax errors ;)

I just compared the ItemID in this case. Maybe you want something else?!

more ▼

answered Aug 18 '12 at 01:55 AM

Bunny83 gravatar image

Bunny83 
66.3k  22  68  267

You are brilliant. This worked like a dream, albeit with some modification to my inventory managing but it worked out perfectly. This is where I was trying to go with my script, but I had no idea how to approach this. Thank you very much for showing me.

Aug 18 '12 at 03:01 AMmactinite77
0

Take a look, maybe you can do something like this(C# Code):

  1. public class Items
  2. {
  3. public int life;
  4. public int price;
  5. public int weight;
  6. }
  7.  
  8. //Creating our List
  9. public List<Items> inventory = new List<Items>();
  10.  
  11. void Start()
  12. {
  13. //Creating some items to put in the list
  14. Items potion = new Items();
  15. potion.life = 50;
  16. potion.price = 70;
  17. potion.weight = 10;
  18.  
  19. Items sword = new Items();
  20. sword.life = 150;
  21. sword.price = 350;
  22. sword.weight = 100;
  23.  
  24. //Adding them to the list
  25. inventory.Add(potion);
  26. inventory.Add(sword);
  27.  
  28. //Searching for the sword
  29. foreach (Items i in inventory)
  30. {
  31. if(i == sword)
  32. {
  33. Debug.Log(i.price); //Loging the current price
  34. i.price = 8001; // Chanching the proce
  35. }
  36. }
  37.  
  38. Debug.Log(sword.price); // Loging the swrod price to double check our implementation works!
  39. }
  40. }

(Tested)

more ▼

answered Aug 18 '12 at 02:20 AM

Paulo Henrique gravatar image

Paulo Henrique 
876  13  20  26

Your answer







The code is like http://www.burgzergarcade.com/. Thanks to his tutorials i was able to start a game. I have been modifying and changing so it can become my own game. i would really like to be able to stack items, since my inventory will have infinity items. I am not sure how to start with the code to check if item is same vs quantity vs stack number.

May 28 '13 at 11:00 PMssoulreaper
  1. public class Item
  2. {
  3. public int itemID;
  4. public string name = "";
  5. public int level;
  6. public bool stackable = true;
  7. public int quantity = 1;
  8. public int maxQuantity = 99;
  9. public int stack = 1;
  10. public int maxStack = 5;
  11. public int attack;
  12. public int physicalDefense;
  13. public int charges;
  14. public int cost;
  15. public string description = "";
  16. public Texture2D icon;
  17. }
  18.  
  19. public static class ItemGenerator
  20. {
  21.  
  22. public static Weapon AddMeleeWeapon(ItemsNames meleeWeaponName, int quantity)
  23. {
  24. Weapon meleeWeapon = new Weapon();
  25.  
  26. switch(meleeWeaponName)
  27. {
  28. case ItemsNames.Sword:
  29. meleeWeapon.itemID = 1;
  30. meleeWeapon.name = "Sword";
  31. meleeWeapon.level = 1;
  32. meleeWeapon.stackable = true;
  33. meleeWeapon.quantity = quantity;
  34. meleeWeapon.maxQuantity = 99;
  35. meleeWeapon.stack = 1;
  36. meleeWeapon.maxStack = 5;
  37. meleeWeapon.attack = 10;
  38. meleeWeapon.cost = 5;
  39. meleeWeapon.description = "Iron Sword";
  40. break;
  41. default:
  42. break;
  43. }
  44.  
  45. meleeWeapon.icon = Resources.Load( "Item/Icon/Weapon/Melee/" + "Sword" ) as Texture2D;
  46.  
  47. return meleeWeapon;
  48. }
  49. }
  50.  
  51. public class Chest : MonoBehaviour
  52. {
  53. public List<Item> loot = new List<Item>();
  54.  
  55. void Start()
  56. {
  57. loot.Add(ItemGenerator.AddMeleeWeapon( ItemsNames.Sword ));
  58. loot.Add(ItemGenerator.AddMeleeWeapon( ItemsNames.Sword ));
  59. loot.Add(ItemGenerator.AddMeleeWeapon( ItemsNames.Sword ));
  60. loot.Add(ItemGenerator.AddMeleeWeapon( ItemsNames.Sword ));
  61. }
  62. }
  63.  
  64. public class PC : BaseCharacter(MonoBehavior)
  65. {
  66. private List<Item> inventory = new List<Item>();
  67.  
  68. public List<Item> Inventory
  69. {
  70. get { return inventory; }
  71. set { inventory = value; }
  72. }
  73. }
  74.  
  75. public class MyGUI : MonoBehaviour
  76. {
  77.  
  78. private void LootWindow(int id)
  79. {
  80.  
  81. for(int cnt = 0; cnt < chest.loot.Count; cnt++)
  82. {
  83. if(GUI.Button(new Rect(5 +(50 * cnt), 10, 50, 50), new GUIContent(chest.loot[cnt].icon)))
  84. {
  85. PC.Instance.Inventory.Add(chest.loot[cnt]);
  86. chest.loot.RemoveAt(cnt);
  87. }
  88. }
  89. }
  90.  
  91. public void InventoryWindow(int id)
  92. {
  93. int cnt = 0;
  94.  
  95. for(int y = 0; y < 15; y++)
  96. {
  97. for(int x = 0; x < 10; x++)
  98. {
  99. if(cnt < PC.Instance.Inventory.Count)
  100. {
  101. if(GUI.Button(new Rect(5 + ( x * buttonWidth ), 20 + (y * 50), 50, 50), new GUIContent(PC.Instance.Inventory[cnt].icon)))
  102. {
  103. }
  104. }
  105. else
  106. {
  107. GUI.Button(new Rect(10 + x * 50, 25 + y * 50, 50, 50), "");
  108. }
  109.  
  110. cnt++;
  111. }
  112. }
  113. }
  114. }

This is Trimmed down to explain what i have.


Trackback 0 And Comment 0

delegate, action, event 활용 관련

|

음.. 저도 처음에 .net freamwork generic관련 함수들을 만났때 이해하기 상당히 어려웠던기억이.... 
검색을 하거나 외부 플러그인 소스를 살펴보면 
delegate, action, event가 자주 눈에 띄는데,  <- 이부분을 직접 분석해가면서 공부했던게 큰도움이 되었습니다. 

아주간략하게 예를 원하시면. 

event의경우 delegate를 담을 그릇으로 보시면되고 
delegate의경우 method를 담는그릇으로 보시면 됩니다. 머 c++의 함수포인터와 거의 비슷한놈이라 보시면 됩니다. 
  
만약에 게임전체에서 사용될 POPUP(YES , NO버튼을 가진)에서 사용될 popup class를 구현할시 

public class Popup : Pannel{ 

public delegate void PopUpEvent(); // void 타입 delegate 선억 
public event PopUpEvent ClickYes; // yes클릭시 실행할 event선언 
public event PopUpEvent ClickNo; // no클릭시 실행할 event선언 
public void ClickYes(){ ClickYes(); } 
public void ClickNo(){ ClickNo(); } 


이런식으로 구현을 해두면 추후에 popup을 활용히 void()타입의 method는 뭐든지 (yes/no버튼의) 
콜백 이벤트로 사용할수 있습니다. 

public void testEvent(){ 
// yes 버튼 눌렸을때 실행 


이런식의 void 형 method를 콜백으로 지정해주고싶으시면 

ClickYes += testEvent; 이런식으로 담을수있습니다. 
ClickYes -= testEvent; 이런식으로 뺄수도 있구요 ^^ 






Class 1 

public delegate void DelegateResultCallback(bool flag); 
DelegateResultCallback _delegateCallback; 

    //콜백 등록 
    public void SetCallBack(DelegateResultCallback callback) 
    { 
        if (_delegateCallback == null) 
        { 
            _delegateCallback = callback; 
        } 
    } 


Class 2 

Class1.SetCallBack(new Class1.DelegateResultCallback(OnResult)); 

//콜백 리턴 함수 구현 
void OnResult(bool flag) 







delegate는 쓰기 나름이지만 대체적으로 게임에서는 이벤트 처리에서 많이 쓰입니다. 예를들어 플레이어를 어떤 몹이 공격을 합니다. 단순히 체력만 깍는 문제면 monster.attack(player) 같은 함수를 만들어 쓰면 되지만 몹이 공격에 대한 반응이 체력도 깍이면서 상태 이상도 일으켜야 되고 넉백도 되면서... 등등 요구에따라 계속 반응이 늘어갈 경우 매 함수를 다 만드는것 보단 딜리게이트를 플레이어에 선언하고 다음과 같이 씁니다. 

public delegate void MobInteraction(interaction_type type); 
public MobInteraction mobinteractions; 
몹이 타격시 player.mobinteractions(짱쌘공격); 

이후 새로운 처리가 필요 할때 마다 이런 식으로 추가만 해주면 되어 확정성이 좋아 집니다. 
동적으로 처리를 빼고 끼고 할 수있어서 재미있는 반응을 게임상에서 만들 수도 있죠..(특정 트리거에가면 저주가 발동해서 저주 반응 처리기를 추가하는 등) 
void KnockBackProcesser(interaction_type type) 

 if(type == 짱쌘공격) 
  { 
    //넉백처리 
  } 

player.mobinteractions += KnockBackProcesser; 




음.. 제대로 설명할 자신은 별로 없지만.. 
delegate(event도 연관되죠..)는 C++에서 CallBack 함수하고 같은 기능을 합니다. 

[실생활에서 delegate와 비슷한 상황을 말한다면..] 
1) 손님이 카페에 갑니다. 
2) 손님이 카페에서 주문을 합니다. 
3) 손님이 주문하면 점원은 결재를 하고 원격으로 call 하는 기계(이름 몰라요..;;)를 손님에게 줍니다. 
    기계안에 delegate로 호출 event를 등록 하고 줬습니다. 
4) 카페에서는 누가 주문을 했는지는 모르고 주문번호(기계에 붙은 번호)와 들어온 걸 만듭니다. 
5) 점원이 주문한 내용을 다 만들고 나면, 주문자가 누군지는 모르지만 주문번호의 기계를 호출(call) 합니다. 
6) 손님은 누가만들었는지는 몰라도 기계가 울리니깐 카운터로 가서 주문한 음료를 받아서 돌아갑니다. 

[게임에서라면..] 
간단하게 GameManager와 Player가 있다고 하겠습니다. 
Player가 죽는 경우의 처리에 대해서 어떻게 처리할 것인가..만 간단히 생각해 보겠습니다. 
예를 드는거라 두가지만 간단하게 극단적으로 말하자면.. 

1) GameManager의 Update에서 Player의 HP를 항상 체크하면서, 혹은 죽었는지를 항상 체크하면서 죽으면 어떤 처리를 하는 방식.(매 Update 주기마다 호출함) 

2) GameManager가 Player에게 delegate로 Dead event를 넘겨줍니다. 그 후에 Player 자신이 죽었을 때 Dead event를 발생(1회 호출함)시켜서 GameManager에게 죽었다고 알립니다. 


delegate-event를 사용하면, 위에서 보시다시피 2)번이 호출 횟수도 적고 로직상 연관성이나 상하관계를 안가지더라도 무언가 이벤트가 일어났을 때 알려줄 수 있습니다. 

좀 극단적인 비교였습니다만.. 
대충 이럴 때 사용하는구나..라고 생각하심이.. =_= 

P.s. 더 자세한 설명은 아랫 분이 해주실거에요. (..)





델리게이트는 보통이벤트핸들링, 리스닝(통칭 이벤트 핸들링이라 하겠습니다)을 위해서 씁니다. 
이벤트핸들링에는 대충 몇가지 의미가 있는데, 크게 보면 모두 다 피동적인 액션을 취할때 사용합니다. 
누군가가 나를 호출하므로서 내가 호출당하기 위해 사건에 개입하는 일이 없게됩니다. 
즉, 사건과 그로인한 행위를 분리할 수 있는것입니다. 

더욱이 델리게이트는 C++에서 이 이벤트핸들링을 위해서 사용하던 함수포인터라는 개념을 더더욱 쓰기좋게 만들어둔 형태로 
여러가지 확장기능이나 편의기능이 제공됩니다. 

몇가지 예를 들어드리면 다음과 같습니다. 
버튼을 누르면 월드내 생성되는 오브젝트 전체가 죽는다고 가정합시다. 
이걸 이벤트핸들링 없이 처리하면, 버튼이 눌렸을 시점을 IF문등으로 체크하여 
모든 오브젝트를 돌며 죽는 처리를 해야할 것입니다. 

foreach(WorldInObject obj in ..) 
    obj.die(); 


그러나 델리게이트를 사용하면, 이것에 대한 코드를 안짜도 됩니다. 
모든 오브젝트의 어미에 미리 void OnButtonPressed(){ this.die(); } 를 선언하고 관장하는쪽에 등록 해두기만하면 전혀 호출할 필요가 없습니다. 
왜냐하면, 버튼이 눌렸을 경우 버튼을 관장하는 매니저에서 OnButtonPressed를 자동으로 호출할 것이기 때문입니다. 

그리고 그 것을 호출하는데에는 우리가 아니더라도 그쪽에서 for문을 돌필요도 없습니다. 하나의 행위자에 같은 행위를 지닌 수많은 오브젝트를 포함할 수 있기때문이죠.(잘은 모르겠지만 대리자(delegate)위임이라는 기능일겁니다.) 

우리가 해야할일은 매니저의 OnButtonPressed 행위자에 우리 오브젝트를 등록해주면됩니다. 


매니저는 아마 다음처럼 구현되어있을겁니다. 

class ButtonManager{ 
delegate void ButtonPressedEvent(); 

// 델리게이트를 사용하기위한 변수입니다.(변수이자 함수로 봐도 무방합니다) 
ButtonPressedEvent onButtonPressed; 

// 이벤트를 핸들링 하기위해 버튼매니저에 오브젝트를 등록받는 함수입니다. 
void Regist(WorldInObject obj){ 
    this.onButtonPressed += obj.OnButtonPressed; 


// 이렇게도 등록할 수 있습니다. 
void Regist(ButtonPressedEvent buttonPressedEvent){ 
    this.onButtonPressed += buttonPressedEvent; 


// 이것도 버튼을 주관하는 다른곳에서 쏴주는 거라고 칩시다. 
void OnButtonPressed(){ 
    this.onButtonPressed(); 


이걸 메서드 위임이라고 합니다. 그밖에도 다양한 기능들이 있는데요. 
저도 사실 델리게이트를 잘 안써서 이런식으로 써본적은 없지만 기본적인 사용예는 되었으리라고 봅니다. 
(델리게이트의 사용법이나 용어들이 좀 틀렸을수도 있는데 일단 말씀드리고자 하는 포인트는 맞으니 그부분만 봐주시면 됩니다;) 

조금 더 나가볼까요? 
위의 코드는 오브젝트 내의 메서드를 전달해서 오브젝트 입장에서는 피동적으로 메서드가 호출될 것입니다. 
그 호출되는 메서드를 상속없이도 동적으로 변경할 수도 있습니다. 

class WorldInObject{ 
    ButtonManager.ButtonPressedEvent OnButtonPressed = null; 


저 객체가 생성될 시점에서... 
WorldInObject obj = new WorldInObject(); 
obj.OnButtonPressed = new delegate(){ 
  // obj.die(); // 아까전의 this.die(); 와 같습니다. 
  obj.alive(); // 살아나는것으로 바뀌었네요. 

  // 즉, 상속하지 않고도 이제 객체마다 다른 행위를 할 수 있게 되었습니다! 


델리게이트를 잘 사용하면 이벤트 규격(함수형태)만 잘 정의하면 필요한정보를 필요한 오브젝트 전원에게 
힘들이지 않고 전달하는 일이 가능합니다. 

가령, 객체집합군 중에서 일정의 객체에만 특정한 무언가를 전달하기위해서 복잡한 코딩을 안해도 됩니다. 
당연히 포문같은걸 돌 필요도 없구요... 어찌보면 함수를 변수화한다는 모든 개념이 적용될 수 있기때문에 
행위의 바리에이션이 좀 더 다양해지면서도 코드의 구축에 힘들일 필요가 없어집니다. 
(단순한 이벤트리스너라면 여기서 하드코드가 안섞일 수 없겠지만 델리게이트는 그런식으로 작업하지 않아도 좋은 결과를 낼 수가 있을것입니다.) 

사용법은 무궁무진한데 갑자기 쓰려니까 딱히 정리가 안되네요. 
C#을 사용하는 개발에서 델리게이트 안쓰면 좀 손해가 큽니다. 

아무튼 이정도만해도 상당히 유용한것을 아시리라 봅니다.



만족하셨다니 다행입니다. 
아침에 쓰고 집에가서 보니 조금 설명이 부족한 부분이 있는데요. 
물론 따로 쓰지 않아도 이해하셨겠지만은... 

버튼매니저의 이 부분에서... 

void OnButtonPressed(){ 
    this.onButtonPressed(); 


다시 설명드리면 ButtonManager.OnButtonPressed 함수 역시 다른곳에서 호출하는 함수이며(왜냐하면 버튼이 눌렸는지 체크하는 루틴은 반드시 모든 코드 내에 하나는 있어야되는데 쓰기가 번거로워서 이놈도 다른 버튼이 눌렸는지 체크하는 곳에서 체크되는 이벤트 핸들러라고 썼습니다;;) 

이 함수가 호출될때 매니저 내의 델리게이트를 함수로 호출하여, 델리게이트에 등록된(+=) 다른 객체의 동일 포맷의 함수를 
같이 호출시켜주는 겁니다. 

이부분이 좀 미흡한듯하여 보충합니다.


Trackback 0 And Comment 0

유니티 안드로이드 확장 개발 - jar 파일(이클립스연동)

|

유니티는 다양한 에셋들을 사용해서 개발의 편의성을 높이고 있습니다.

Prime31이나 각종 애드몹 관련 플러그인들도 이와 같이 개발의 편의성을 높이는 플러그인입니다. 이런 플러그인을 직접 돈주고 사면 좋겠지만.

저희 같은 가난한 개발자 및 학생들은 직접 유니티와 자바의 연동을 통해서 구현해야됩니다. 


여기서는 자바 클래스와 연동하는 방법을 간단하게 소개하겠습니다.


각각 호출할때 기본적인 함수들이 있습니다.


유니티에서는 자바 클래스를 호출할때

AndroidJavaObject.Call 로 자바 클래스의 함수를 호출할수 있습니다.


자바클래스에서 유니티를 호출할때는

UnityPlayer.UnitySendMessage 함수를 사용하여 호출합니다.


유니티로 제작된 게임을 자바 클래스를 통해 확장하는 방식은 2가지가 있는데

첫번째 방식은 자바 클래스를 별도로 제작한 후 유니티 프로젝트 폴더의 Plugins/Android 폴더에 올려서 확장하는 방식이고

두번째 방식은 유니티 내에서 이클립스로 export해주어서 확장하는 방법입니다.


jar 파일을 생성해서 관리하는것이 좀더 편한것 같으니 첫번째 방식을 사용하겠습니다.


먼저 jar 파일을 생성할 프로젝트를 이클립스에서 생성해줍니다.







패키지 이름을 com.test.androidjartest 로 지정했는데 유니티 내의 Bundle Identifier 항목과 일치해야됩니다.


Edit - Project Settings - Player 에서

안드로이드 모양의 버튼을 누르면 Bundle Identifiire 를 수정할수 있습니다. 위에서 만든 패키지 이름과 일치 시켜줍니다.


이클립스에서 프로젝트를 생성하고 난다음에는 유니티가 제공하는 jar 파일을 임포트 해주어야 유니티가 제공해주는 함수를 사용할수 있습니다.


유니티가 제공하는 jar 파일은 classes.jar 파일이며 경로는 설치된 폴더에서 Editor/Data/PlaybackEngines/androidplayer/bin 폴더에 존재합니다.






jar 파일을 추가하는 방법은 프로젝트 폴더에서 오른쪽 버튼을 누르고 properties 를 누릅니다.

그리고 JaVa Build Path 에서 Libraries 에서 Add External JARs 버튼을 누르고 해당 경로에 있는 jar 파일은 선택해주고

Order and Export 에서 불러온 jar 파일을 체크해주고 ok를 누릅니다.





이클립스에서 만들어진 MainActivity 를 위와 같이 고쳐줍니다. 소스를 모두 수정하면 이 소스를 jar 파일로 만들어 exprot 해주겠습니다.






프로젝트 폴더에서 오른쪽 버튼을 누르고 Exprot 를 클릭해줍니다. 그리고 위 처럼 Jar file을 누르고 next 버튼을 눌러주고

자신의 프로젝트에서 src 폴더만 체크 해주고 Jar File 의 이름을 설정해주고 finish 버튼을 누르면 jar 파일이 완성됩니다.





이제 유니티로 넘어오겠습니다.


유니티 내에서는 jar 파일을 인식하기 위해서는 반드시 해당 경로에 jar 파일이 존재해야됩니다. 안드로이드 같은 경우네는

Plugins/Android 폴더에 jar 파일이 존재해야됩니다.


그리고 해당 jar 파일을 실행하기 위해서는 AndoridManifest 및 res 폴더가 필요하므로 위에서 만든 

jar 파일과 res 폴더 AndroidManifest 파일들을 Android 폴더에 집어 넣어줍니다.






이제 모든 세팅이 완료 되었습니다.



이제 제대로 유니티에서 -> 자바 클래스로 자바 클래스에서 유니티로 함수가 호출되는지 보겠습니다.


빈게임 오브젝트를 만들고 AndroidManager로 이름을 변경합니다.(반드시 AndroidManager로 지어주어야 됩니다.)


그리고 AndroidManager 에 아래 스크립트를 할당해줍니다.





AndroidManager 내의 Start 함수에서 MainActivity 자바 클래스의 HelloFunction 함수를 호출(_activity,Call("HelloFunction)하고,

자바 클래스에서는(이클립스) AndroidManage 클래스의 AndroidLog 함수를 호출(UnityPlayer.UnitySendMessage("AndroidManager", "AndroidLog", "Hello!");)하는 구조입니다.



할당후 안드로이드 단말기에서 빌드를 하면 아래와 같이 로그를 출력할 것입니다.






Trackback 0 And Comment 3
  1. Hello 2014.08.04 17:17 address edit & del reply

    AndroidManager.cs 말고 AndroidManager_Test.cs 로 만들면 안되는 이유가 뭐죠?>

    • Favicon of https://hyunity3d.tistory.com BlogIcon 히아레인 2014.08.04 22:03 신고 address edit & del

      MainActivity 보시면 unitysendMessage 함수에서 해당 게임오브젝트 명을 입력해야됩니다 AndroidManager_Test 라고 입력해보세요

  2. Ku 2014.10.21 23:04 address edit & del reply

    실행하였다니 아래와 같은 오류가 발생하였습니다 ㅠㅠ 왜인지 감이 안옵니다 ㅠㅠㅠㅠㅠㅠ
    Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for details.
    C:\Users\kyoungku\Desktop\Company\adt\sdk\build-tools\android-4.4W\aapt.exe package --auto-add-overlay -v -f -m -J gen -M AndroidManifest.xml -S "res" -I "C:/Users/kyoungku/Desktop/Company/adt/sdk/platforms/android-20\android.jar" -F bin/resources.ap_

    stderr[
    res\values\styles.xml:7: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'.
    res\values-v11\styles.xml:7: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'.
    res\values-v14\styles.xml:8: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light.DarkActionBar'.
    ]
    stdout[
    Configurations:
    (default)
    v11
    v14
    hdpi
    mdpi
    xhdpi
    xxhdpi
    w820dp

    Files:
    drawable\app_icon.png
    Src: () res\drawable\app_icon.png
    drawable\ic_launcher.png
    Src: (hdpi) res\drawable-hdpi\ic_launcher.png
    Src: (mdpi) res\drawable-mdpi\ic_launcher.png
    Src: (xhdpi) res\drawable-xhdpi\ic_launcher.png
    Src: (xxhdpi) res\drawable-xxhdpi\ic_launcher.png
    layout\activity_main.xml
    Src: () res\layout\activity_main.xml
    menu\main.xml
    Src: () res\menu\main.xml
    values\dimens.xml
    Src: () res\values\dimens.xml
    Src: (w820dp) res\values-w820dp\dimens.xml
    values\strings.xml
    Src: () res\values\strings.xml
    values\styles.xml
    Src: () res\values\styles.xml
    Src: (v11) res\values-v11\styles.xml
    Src: (v14) res\values-v14\styles.xml
    AndroidManifest.xml
    Src: () AndroidManifest.xml

    Resource Dirs:
    Type drawable
    drawable\app_icon.png
    Src: () res\drawable\app_icon.png
    drawable\ic_launcher.png
    Src: (hdpi) res\drawable-hdpi\ic_launcher.png
    Src: (mdpi) res\drawable-mdpi\ic_launcher.png
    Src: (xhdpi) res\drawable-xhdpi\ic_launcher.png
    Src: (xxhdpi) res\drawable-xxhdpi\ic_launcher.png
    Type layout
    layout\activity_main.xml
    Src: () res\layout\activity_main.xml
    Type menu
    menu\main.xml
    Src: () res\menu\main.xml
    Type values
    values\dimens.xml
    Src: () res\values\dimens.xml
    Src: (w820dp) res\values-w820dp\dimens.xml
    values\strings.xml
    Src: () res\values\strings.xml
    values\styles.xml
    Src: () res\values\styles.xml
    Src: (v11) res\values-v11\styles.xml
    Src: (v14) res\values-v14\styles.xml
    Including resources from package: C:\Users\kyoungku\Desktop\Company\adt\sdk\platforms\android-20\android.jar
    applyFileOverlay for drawable
    applyFileOverlay for layout
    applyFileOverlay for anim
    applyFileOverlay for animator
    applyFileOverlay for interpolator
    applyFileOverlay for transition
    applyFileOverlay for xml
    applyFileOverlay for raw
    applyFileOverlay for color
    applyFileOverlay for menu
    applyFileOverlay for mipmap
    Processing image: res\drawable\app_icon.png
    Processing image: res\drawable-hdpi\ic_launcher.png
    Processing image: res\drawable-mdpi\ic_launcher.png
    Processing image: res\drawable-xhdpi\ic_launcher.png
    (processed image res\drawable-mdpi\ic_launcher.png: 82% size of source)
    Processing image: res\drawable-xxhdpi\ic_launcher.png
    (processed image res\drawable-hdpi\ic_launcher.png: 77% size of source)
    (processed image res\drawable\app_icon.png: 94% size of source)
    (processed image res\drawable-xhdpi\ic_launcher.png: 74% size of source)
    (processed image res\drawable-xxhdpi\ic_launcher.png: 72% size of source)
    (new resource id app_icon from drawable\app_icon.png #generated)
    (new resource id ic_launcher from hdpi\drawable\ic_launcher.png #generated)
    (new resource id ic_launcher from mdpi\drawable\ic_launcher.png #generated)
    (new resource id ic_launcher from xhdpi\drawable\ic_launcher.png #generated)
    (new resource id ic_launcher from xxhdpi\drawable\ic_launcher.png #generated)
    (new resource id activity_main from res\layout\activity_main.xml)
    (new resource id main from res\menu\main.xml)
    ]

인앱빌링 구현하기 ver3 - 구글번역

|

Implementing In-app Billing (IAB Version 3)

구글 플레이에 인앱 결제는 인앱 결제 요청을 전송하고 구글은 재생 사용하여 응용 프로그램 내 결제 트랜잭션을 관리하기위한 간단, 간단한 인터페이스를 제공합니다. 아래 정보는 버전 3 API를 사용하여 앱내 결제 서비스 응용 프로그램에서 호출을하는 방법의 기초를 다룹니다.

Note: 완전한 구현을 확인하고 응용 프로그램을 테스트하는 방법에 대해, 인 - 앱 판매 제품 교육 클래스를 참조하십시오.교육 과정은 당신의 연결을 설정하는 구글 플레이에서 결제 요청과 응답의 처리를 보내, 당신은 인 - 앱 결제를 할 수 있도록 배경 스레드 관리와 관련된 주요 작업을 처리 할 수있는 편리한 클래스를 포함하는 전체 샘플 앱 결제 응용 프로그램을 제공합니다 main Activity 에서 호출합니다.

당신이 시작하기 전에, 당신은 쉽게 당신이 인 - 앱 결제를 구현할 수 있도록합니다 개념에 익숙해 앱내 결제 개요를 읽을 수 있는지 확인하십시오.

 응용 프로그램에 위치한 앱 결제를 구현하려면 다음을 수행해야합니다 :

  1. 프로젝트에 앱내 결제 라이브러리를 추가합니다.
  2. 여러분의 AndroidManifest.xml 파일을 업데이트합니다.
  3. ServiceConnection를 만들고 IInAppBillingService에 바인딩합니다.
  4. 응용 프로그램 toIInAppBillingService에서 인 - 앱 결제 요청을 보낼 수 있습니다.
  5. 구글 플레이에서 인 - 앱 결제 응답을 처리합니다.

프로젝트에 AIDL 파일을 추가


IInAppBillingService.aidl은 인 - 앱 과금 버전 3 서비스에 대한 인터페이스를 정의하는 안드로이드 인터페이스 정의 언어 (AIDL) 파일입니다. 당신은 IPC 메서드 호출을 호출하여 결제 요청을 할이 인터페이스를 사용합니다.

AIDL 파일을 얻으려면 :

  1. Android SDK Manager 엽니다.
  2. SDK Manager에 expand the Extras 을 선택합니다.
  3. Google Play Billing Library 을 선택합니다..
  4. 다운로드를 완료하기 위해 패키지 설치를 클릭합니다.

IInAppBillingService.aidl 파일은 다음 경로에 설치됩니다.  <sdk>/extras/google/play_billing/.

프로젝트에 AIDL를 추가하려면 :

  1. 안드로이드 프로젝트에 IInAppBillingService.aidl 파일을 복사합니다.
    • 이클립스를 사용한다면:
      1. If you are starting from an existing Android project, open the project in Eclipse. If you are creating a new Android project from scratch, click File > New > Android Application Project, then follow the instructions in the New Android Application wizard to create a new project in your workspace.
      2. In the /src directory, click File > New > Package, then create a package namedcom.android.vending.billing.
      3. Copy the IInAppBillingService.aidl file from <sdk>/extras/google/play_billing/ and paste it into the src/com.android.vending.billing/ folder in your workspace.
    • 이클립스가 아닌 다른 환경을 사용한다면: Create the following directory/src/com/android/vending/billing and copy the IInAppBillingService.aidl file into this directory. Put the AIDL file into your project and use the Ant tool to build your project so that theIInAppBillingService.java file gets generated.
  2. 응용 프로그램을 빌드합니다. 당신은 당신의 프로젝트의 / gendirectory에 IInAppBillingService.java라는 생성 된 파일을 볼 수 있습니다.

Updating Your Application's Manifest


인앱 결제는 구글이 응용 프로그램과 구글 플레이 서버 사이의 모든 통신을 처리하는 응용 프로그램을 재생에 의존합니다.구글 재생 응용 프로그램을 사용하려면 응용 프로그램이 적절한 권한을 요청해야합니다. 당신은 당신의 AndroidManifest.xml 파일에 com.android.vending.BILLING 권한을 추가하여이 작업을 수행 할 수 있습니다. 응용 프로그램이 앱내 결제 권한이 있지만, 결제 요청을 보내려고 시도를 선언하지 않는 경우, 구글의 플레이는 요청을 거부하고 오류로 응답합니다.

To give your app the necessary permission, add this line in your Android.xml manifest file:

<uses-permission android:name="com.android.vending.BILLING" />

Creating a ServiceConnection


응용 프로그램은 응용 프로그램과 구글 플레이 사이의 메시징을 촉진하기 ServiceConnection이 있어야합니다. 최소한 응용 프로그램은 다음을 수행해야합니다 :

  • IInAppBillingService에 바인딩합니다.
  • 구글 응용 프로그램을 재생하려면 (IPC 메서드 호출 등)의 결제 요청을 보낼 수 있습니다..
  • 각 대금 청구 요청과 함께 반환되는 동기 응답 메시지를 처리 할 수 ​​있습니다.

Binding to IInAppBillingService

구글 플레이에 앱내 결제 서비스와의 연결을 설정하려면, IInAppBillingService에 활동을 결합하는 ServiceConnection를 구현합니다.연결이 설정된 후 IInAppBillingService 인스턴스에 대한 참조를 얻을 수있는 onServiceDisconnected 및 onServiceConnected 방법을 재정의합니다. 

IInAppBillingService mService;

ServiceConnection mServiceConn = new ServiceConnection() {
   
@Override
   
public void onServiceDisconnected(ComponentName name) {
       mService
= null;
   
}

   
@Override
   
public void onServiceConnected(ComponentName name,
     
IBinder service) {
       mService
= IInAppBillingService.Stub.asInterface(service);
   
}
};

귀하의 활동의에서 onCreate 메서드에서 bindService 메서드를 호출하여 바인딩을 수행합니다.앱내 결제 서비스 및 사용자가 만든 ServiceConnection의 인스턴스를 참조하는 방법 anIntent을 전달합니다.

@Override
public void onCreate(Bundle savedInstanceState) {    
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.activity_main);        
    bindService
(new
       
Intent("com.android.vending.billing.InAppBillingService.BIND"),
                mServiceConn
, Context.BIND_AUTO_CREATE);

이제 구글 플레이 서비스와 통신하는 mService 참조를 사용할 수 있습니다. 위 해당 함수 같은 경우

구글 샘플 ver3 IAPHelper 클래스에 정의 되있음.


Important: 당신이 당신의 활동을 완료하면 앱내 결제 서비스에서 바인딩을 해제해야합니다. 당신이 바인딩을 해제하지 않을 경우, 오픈 서비스 연결 장치의 성능이 저하 될 수 있습니다. 이 예제에서는 활동의들의 OnDestroy 메서드를 재정 의하여 mServiceConn라는 앱내 결제에 대한 서비스 연결에 바인딩 해제 작업을 수행하는 방법을 보여줍니다.

@Override
public void onDestroy() {
   
super.onDestroy();
   
if (mService != null) {
        unbindService
(mServiceConn);
   
}  
}

IInAppBillingService에 결합 서비스 연결의 전체 구현을 위해, 인 - 앱 판매 제품 교육 클래스 및 관련 샘플을 참조하십시오.

Making In-app Billing Requests


응용 프로그램이 구글 플레이에 연결 한 후에는 응용 프로그램 내 제품의 구매 요청을 시작할 수 있습니다. 구글 플레이는 사용자가 자신의 지불 방법을 입력 할 수있는 체크 아웃 인터페이스를 제공하여 응용 프로그램이 직접 지불 트랜잭션을 처리 할 필요가 없습니다.항목이 구입되면, 구글의 플레이는 사용자가 해당 항목의 소유권을 가지고 있으며, 그것이 소비 될 때까지 동일한 제품 ID와 다른 아이템을 구입하는 사용자를 방지 함을 인식합니다. 당신은 항목이 응용 프로그램에서 소비되는 방법을 제어하고, 구글은 다시 구입을위한 항목을 사용할 수 있도록 재생 알릴 수 있습니다. 또한 구글은 사용자가 빠르고 만들어진 구매 목록을 검색 재생 조회 할 수 있습니다. 당신은 당신의 사용자가 응용 프로그램을 시작할 때 사용자의 구매를 복원하려는 경우에 유용합니다.

Querying for Items Available for Purchase

응용 프로그램에서, 당신은 앱내 결제 버전 3 API를 사용하여 Google 플레이에서 항목 상세 정보를 조회 할 수 있습니다. 먼저 각 문자열이 살 수있는 항목에 대한 제품 ID입니다 키 "ITEM_ID_LIST"와 제품 ID의 문자열 ArrayList에 포함 된 번들을 작성, 인 - 앱 결제 서비스에 대한 요청을 전달합니다.

ArrayList<String> skuList = new ArrayList<String> ();
skuList
.add("premiumUpgrade");
skuList
.add("gas");
Bundle querySkus = new Bundle();
querySkus
.putStringArrayList(“ITEM_ID_LIST”, skuList);

구글 플레이에서이 정보를 검색 앱내 결제 버전 3 API에 getSkuDetails의 메서드를 호출하는 방법에게 인 - 앱 결제 API 버전 ( "3")를 전달하려면, 당신의 전화 응용 프로그램, 구입 형태의 패키지 이름 ( "inapp"), 당신이 만든 번들.

Bundle skuDetails = mService.getSkuDetails(3, 
   getPackageName
(), "inapp", querySkus);

만약 결과가 성공하면 리턴해준다. BILLING_RESPONSE_RESULT_OK (0) 값을.

Warning: 주 스레드에 getSkuDetails 메서드를 호출하지 마십시오. 이 메서드를 호출하면 주 스레드를 차단할 수있는 네트워크 요청을 트리거합니다. 대신, 별도의 스레드를 만들고 해당 스레드 내부에서 getSkuDetails 메서드를 호출합니다.

구글 플레이에서 가능한 모든 응답 코드를 참조하십시오 인 - 앱 결제 참조 (영문)를 참조하십시오. 

조회 결과가 키 DETAILS_LIST의 String ArrayList에 저장됩니다.구매 정보는 JSON 형식의 문자열에 저장됩니다. 반환되는 제품 상세 정보의 종류를 확인하려면, 앱 결제 참조를 참조하십시오. 

이 예에서 skuDetails 번들에서 - 응용 프로그램 항목에 대한 가격을 검색하는 이전의 코드에서 반환.

int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
   
ArrayList<String> responseList
     
= skuDetails.getStringArrayList("DETAILS_LIST");
   
   
for (String thisResponse : responseList) {
     
JSONObject object = new JSONObject(thisResponse);
     
String sku = object.getString("productId");
     
String price = object.getString("price");
     
if (sku.equals("premiumUpgrade")) mPremiumUpgradePrice = price;
     
else if (sku.equals("gas")) mGasPrice = price;
   
}
}

Purchasing an Item

앱에서 구매 요청을 시작하려면 앱내 결제 서비스에 getBuyIntent 메서드를 호출합니다. 메서드에 전달하는 인 - 앱 결제 API 버전 ( "3"), 사용자 통화 앱, 구매하는 품목에 대한 제품 ID의 패키지 이름, 구입 형태 ( "inapp"또는 "서브") 및 developerPayload 문자열입니다.developerPayload 문자열은 사용자가 구글의 구매 정보와 함께 다시 보낼 플레이 할 추가 인수를 지정하는 데 사용됩니다.

Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
   sku
, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");

요청이 성공하면, 반환 된 번들은 당신이 구입 흐름을 시작하는 데 사용할 수있는 BILLING_RESPONSE_RESULT_OK (0) 및 aPendingIntent의 응답 코드가 있습니다. 구글 플레이에서 가능한 모든 응답 코드를 참조하십시오 인 - 앱 결제 참조 (영문)를 참조하십시오. 다음으로, 키 BUY_INTENT와 응답 번들에서 PendingIntent의 압축을 풉니 다.

PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");

구매 거래를 완료하려면 startIntentSenderForResult 메서드를 호출하고 사용자가 만든 PendingIntentthat를 사용합니다. 이 예에서, 요청 코드 (1001)의 임의의 값을 사용하고있다.

startIntentSenderForResult(pendingIntent.getIntentSender(),
   
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
   
Integer.valueOf(0));

구글 재생 응용 프로그램의는 onActivityResult 메서드로 PendingIntent에 대한 응답을 보냅니다. TheonActivityResult 방법은 Activity.RESULT_OK (1) 또는 Activity.RESULT_CANCELED의 결과 코드를가집니다 (0). 응답 의도에 반환되는 주문 정보의 종류를 확인하려면, 인 - 앱 결제 참조를 참조하십시오. 

주문에 대한 구매 데이터는 예를 들어, 응답 의도에 INAPP_PURCHASE_DATA 키에 매핑 된 JSON 형식의 문자열입니다 :

'{ 
   "orderId":"12999763169054705758.1371079406387615",
   "packageName":"com.example.app",
   "productId":"exampleSku",
   "purchaseTime":1345678900000,
   "purchaseState":0,
   "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
   "purchaseToken":"rojeslcdyyiapnqcynkjyyjh"
 }'

앞의 예에서 계속, 당신은 응답 의도에서 응답 코드, 구매 데이터 및 서명을 얻을..

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   
if (requestCode == 1001) {          
     
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
     
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
     
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
       
     
if (resultCode == RESULT_OK) {
         
try {
           
JSONObject jo = new JSONObject(purchaseData);
           
String sku = jo.getString("productId");
            alert
("You have bought the " + sku + ". Excellent choice,
               adventurer!"
);
         
}
         
catch (JSONException e) {
             alert
("Failed to parse purchase data.");
             e
.printStackTrace();
         
}
     
}
   
}
}

Security Recommendation: When you send a purchase request, create a String token that uniquely identifies this purchase request and include this token in the developerPayload.You can use a randomly generated string as the token. When you receive the purchase response from Google Play, make sure to check the returned data signature, the orderId, and the developerPayload String. For added security, you should perform the checking on your own secure server. Make sure to verify that the orderId is a unique value that you have not previously processed, and the developerPayload String matches the token that you sent previously with the purchase request.

Querying for Purchased Items

To retrieve information about purchases made by a user from your app, call the getPurchases method on the In-app Billing Version 3 service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, and the purchase type (“inapp” or "subs").

Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);

The Google Play service returns only the purchases made by the user account that is currently logged in to the device. If the request is successful, the returned Bundle has a response code of 0. The response Bundle also contains a list of the product IDs, a list of the order details for each purchase, and the signatures for each purchase.

To improve performance, the In-app Billing service returns only up to 700 products that are owned by the user when getPurchase is first called. If the user owns a large number of products, Google Play includes a String token mapped to the key INAPP_CONTINUATION_TOKEN in the response Bundle to indicate that more products can be retrieved. Your application can then make a subsequent getPurchases call, and pass in this token as an argument. Google Play continues to return a continuation token in the response Bundle until all products that are owned by the user has been sent to your app.

For more information about the data returned by getPurchases, see In-app Billing Reference. The following example shows how you can retrieve this data from the response.

int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
   
ArrayList<String> ownedSkus =
      ownedItems
.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
   
ArrayList<String>  purchaseDataList =
      ownedItems
.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
   
ArrayList<String>  signatureList =
      ownedItems
.getStringArrayList("INAPP_DATA_SIGNATURE");
   
String continuationToken =
      ownedItems
.getString("INAPP_CONTINUATION_TOKEN");
   
   
for (int i = 0; i < purchaseDataList.size(); ++i) {
     
String purchaseData = purchaseDataList.get(i);
     
String signature = signatureList.get(i);
     
String sku = ownedSkus.get(i);
 
     
// do something with this purchase information
     
// e.g. display the updated list of products owned by user
   
}

   
// if continuationToken != null, call getPurchases again
   
// and pass in the token to retrieve more items
}

Consuming a Purchase

You can use the In-app Billing Version 3 API to track the ownership of purchased in-app products in Google Play. Once an in-app product is purchased, it is considered to be "owned" and cannot be purchased from Google Play. You must send a consumption request for the in-app product before Google Play makes it available for purchase again.

Important: Managed in-app products are consumable, but subscriptions are not.

How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for in-app products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or equipment). You would typically not want to implement consumption for in-app products that are purchased once and provide a permanent effect (for example, a premium upgrade).

To record a purchase consumption, send the consumePurchase method to the In-app Billing service and pass in the purchaseToken String value that identifies the purchase to be removed. The purchaseToken is part of the data returned in the INAPP_PURCHASE_DATA String by the Google Play service following a successful purchase request. In this example, you are recording the consumption of a product that is identified with the purchaseToken in thetoken variable.

int response = mService.consumePurchase(3, getPackageName(), token);

Warning: Do not call the consumePurchase method on the main thread. Calling this method triggers a network request which could block your main thread. Instead, create a separate thread and call the consumePurchasemethod from inside that thread.

It's your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player's inventory with the amount of currency purchased.

Security Recommendation: You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.

Implementing Subscriptions

Launching a purchase flow for a subscription is similar to launching the purchase flow for a product, with the exception that the product type must be set to "subs". The purchase result is delivered to your Activity'sonActivityResult method, exactly as in the case of in-app products.

Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
   MY_SKU
, "subs", developerPayload);

PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT);
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
   
// Start purchase flow (this brings up the Google Play UI).
   
// Result will be delivered through onActivityResult().
   startIntentSenderForResult
(pendingIntent, RC_BUY, new Intent(),
       
Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}

To query for active subscriptions, use the getPurchases method, again with the product type parameter set to "subs".

Bundle activeSubs = mService.getPurchases(3, "com.example.myapp",
                   
"subs", continueToken);

The call returns a Bundle with all the active subscriptions owned by the user. Once a subscription expires without renewal, it will no longer appear in the returned Bundle.

Securing Your Application


To help ensure the integrity of the transaction information that is sent to your application, Google Play signs the JSON string that contains the response data for a purchase order. Google Play uses the private key that is associated with your application in the Developer Console to create this signature. The Developer Console generates an RSA key pair for each application.

Note:To find the public key portion of this key pair, open your application's details in the Developer Console, then click on Services & APIs, and look at the field titled Your License Key for This Application.

The Base64-encoded RSA public key generated by Google Play is in binary encoded, X.509 subjectPublicKeyInfo DER SEQUENCE format. It is the same public key that is used with Google Play licensing.

When your application receives this signed response you can use the public key portion of your RSA key pair to verify the signature. By performing signature verification you can detect responses that have been tampered with or that have been spoofed. You can perform this signature verification step in your application; however, if your application connects to a secure remote server then we recommend that you perform the signature verification on that server.

For more information about best practices for security and design, see Security and Design.


Trackback 0 And Comment 0

안드로이드 서버 php 통신(간단히)

|

안드로이드가 웹과 통신을 하기 위해선 아래 두가지가 필요하다.


1. 웹 서버 (php)

2. 인터넷 연결이 가능한 안드로이드 단말기 or 안드로이드 시뮬레이터


우리는 안드로이드 단말에서 정말 간단한 php 문을 호출할 생각이다.(나중에 응용가능하게)

웹 서버 같은경우에 apm 이나 wpn-xm 등을 설치해서 구축할수 있다.

설치방법은 구글신에게 물어보자.



php 코드 부분

먼저

값을 주고 받을 php 를 만든다.

아래는 아주 간단히 값을 받고 값을 전달하는 php문이다.


php 파일 이름은 phpTest.php 이다. 텍스트 편집기로 파일을 만들고 저장하자.

필자는 notpad++을 사용했다.


post 방식으로 각각 name, age, sex 라는 변수를 받아와

출력하는 간단한 php 입니다.



해당 php 파일을 자신의 서버에 올립시다.

아래 제가 생각하는 php에 대한 간단한 생각들.


php 통신

-보내주는건 많이 보내줄수있는듯 get 방식이든 post 방식이든 get방식같은경우에 글자수가 제한이 있고 주소창에서 입력이 가능하므로 결국엔 post 방식으로 보내주는게 안전할듯.

받을때는 echo 로 출력된 정보를 받는다고 생각하면 편할거같음. 받는 데이터 같은경우에 웹에있는 데이터를 받는다고 생각됨.

여러개의 데이터를 전송받기 위해서는 echo 에서 구분자를 이용한 방법이나 결국 json 형식으로 받는 수 밖에 없음(아님 xml)



결국 결론은 안드로이드 단말에서 php 서버에 보낼수 있는 변수들은 무제한이고, 받을수 있는 값들은 php 문에 출력되는 echo 문들 이다.

결국 이렇기 때문에 많은 데이터를 받을때는(mysql 사용등) json 형식같은 것을 사용해서 php 값들받게 되는것 같다.



안드로이드 코드부분

우리가 만들 안드로이드 코드는

버튼을 누르면 php 웹에 접속해서 원하는 값을 받는 코드입니다.

제일 먼저 해야될것은 AndroidMainfest에 인터넷이 가능하도록 퍼미션을 설정하는 것입니다.





그리고 active_main.xml 에서 버튼과 텍스트 뷰를 추가합니다.







허니콤이후로 네트워크 접속시 별도의 스레드를 돌려야 오류가 나지 않습니다.

AsyncTask를 사용합니다.







urlpath 에는 자기가 접속할 php 주소를 입력합니다.

onCreate 함수(엑티비티가 처음 시작할때 무조건 실행되는 함수)에서 

버튼과 텍스트뷰를 찾고 버튼을 클릭했을시에 HttpTask를 실행합니다.(AsyncTask)


  bn.setOnClickListener(new Button.OnClickListener(){
            public void onClick(View v) {

              new HttpTask().execute();               
            }           
        });




아래는 AsyncTask 입니다. 세번째 인자의 String를 입력했는데 이 값은 

doInBackground의 반환값이며, onPostExecute의 매개변수가 됩니다.

doInBackground의 반환값이 onPostExcute의 인자로 전달된다고 생각하면 될것 같습니다.

자세한 내용은 AsyncTask로 검색하면 나올것입니다.


php 상에서 post 값으로 받기 때문에 HttpPost 로 선언해주고

넘길 값들을 Vector 에 저장한후 웹으로 보내줍니다.




onPostExcute 에서 텍스트뷰를 변경해줍니다. onPostExcute는 doinBackground 가 실행된후 실행되는 함수이며,

안드로이드 메인 ui 같은경우에는 여기서 변경해주어야 에러가 나지 않습니다.








public class MainActivity extends Activity {

    Button bn;
    TextView result;
   
   
    //접속할주소
    private final String urlPath = "";
    private final String TAG = "PHPTEST";
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {      
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        bn = (Button)findViewById(R.id.btn);
        result = (TextView)findViewById(R.id._result);
       
        bn.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                new HttpTask().execute();   
            }
           
        });
           
    }
   
    class HttpTask extends AsyncTask<Void, Void, String>{

        @Override
        protected String doInBackground(Void... voids) {
            // TODO Auto-generated method stub
            try{
                HttpPost request = new HttpPost(urlPath);
                //전달할 인자들
                Vector<NameValuePair> nameValue = new Vector<NameValuePair>();
                nameValue.add(new BasicNameValuePair("name", "홍길동"));
                nameValue.add(new BasicNameValuePair("age", "24"));
                nameValue.add(new BasicNameValuePair("sex", "male"));               
               
                //웹 접속 - utf-8 방식으로
                HttpEntity enty = new UrlEncodedFormEntity(nameValue, HTTP.UTF_8);
                request.setEntity(enty);
               
                HttpClient client = new DefaultHttpClient();
                HttpResponse res = client.execute(request);
                //웹 서버에서 값받기
                HttpEntity entityResponse = res.getEntity();
                InputStream im = entityResponse.getContent();
                BufferedReader reader = new BufferedReader(new InputStreamReader(im, HTTP.UTF_8));
               
                String total = "";
                String tmp = "";
                //버퍼에있는거 전부 더해주기
                //readLine -> 파일내용을 줄 단위로 읽기
                while((tmp = reader.readLine())!= null)
                {
                    if(tmp != null)
                    {
                        total += tmp;
                    }
                }
                im.close();
                //결과창뿌려주기 - ui 변경시 에러
                //result.setText(total);
                return total;               
            }catch(UnsupportedEncodingException e){
                e.printStackTrace();
            }catch(IOException e){
                e.printStackTrace();
            }
            //오류시 null 반환
            return null;
        }
        //asyonTask 3번째 인자와 일치 매개변수값 -> doInBackground 리턴값이 전달됨
        //AsynoTask 는 preExcute - doInBackground - postExecute 순으로 자동으로 실행됩니다.
        //ui는 여기서 변경
        protected void onPostExecute(String value){
            super.onPostExecute(value);
            result.setText(value);
        }       
    }      
}



Trackback 0 And Comment 11
  1. 구독 2014.05.18 22:53 address edit & del reply

    그..HttpTask가 다른 클래스인데 urlPath가 어떻게 불러와진거죠???

    • Favicon of https://hyunity3d.tistory.com BlogIcon 히아레인 2014.05.19 08:11 신고 address edit & del

      HttpTask 는 MainActivity안에 내부 클래스로 설정하였습니다.

  2. 감사 2014.05.21 20:00 address edit & del reply

    다음과 같은 부분에 컴파일 에러나는데 어떻게 해야하나요?
    - 클릭 리스너 부분에 (view)-> 이부분
    - Vector<~>
    - super.onOptionsItemSelected(item);

  3. Favicon of https://taetanee.tistory.com BlogIcon 테타니 2014.08.16 03:25 신고 address edit & del reply

    똑같이 했는데 실행오류 '*****이(가) 중지되었습니다'가 나네요.
    죄송하지만 프로젝트를 보내주실 수 있는지요ㅠㅠ
    xpxksl@gmail.com
    염치불구하고 부탁드립니다!

    • Favicon of https://hyunity3d.tistory.com BlogIcon 히아레인 2014.08.19 09:49 신고 address edit & del

      최근에 확인해본결과 수정한거라 똑같이 하시면 실행오류 안뜹니다.
      해당 프로젝트 파일은 없습니다.

  4. 좋은글감사해요 2014.10.01 23:51 address edit & del reply

    올려주신 글로 실습중인데요
    버튼클릭시 아무 반응이 없더라구요
    해서 로그를 보니
    HttpHostConnectException: Connection to http://ip주소 .php refused

    ConnectException : http://ip주소.php : connect failed:EHOSTUNREACH
    이렇게 2개의 익셉션이 떠서 보니까
    컴퓨터상에서는 웹브라우져로 접근해서 echo부분확인이 되는데
    안드로이드에서 똑같이 http주소로 접근하니 없는페이지로 뜨더라구요
    웹페이지를 표시할 수 없습니다. 이 화면이요

    구글링으로 방화벽과 dmz? 부분을 해봤는데 여전하네요ㅠㅠ
    혹시 해결방법 아시나요? 라우터문제라고도 하는데.. 혹시 아는부분있으시면 답변 부탁좀 드립니다.
    2일째 붙잡고있네요ㅠㅠ

    • Favicon of https://hyunity3d.tistory.com BlogIcon 히아레인 2014.10.02 09:13 신고 address edit & del

      먼저 해당 주소로 안드로이드 기기에서 접속해 보세요.
      접속이 되나 확인해보시고 안되면 외부에서 접속이 불가능 한겁니다.
      <?php
      echo "test";
      ?>
      이런식으로 php 페이지를 만들어 들어가서 test라는 글자가 잘뜨나 확인하면 됩니다.

      외부에서 접속이 불가능하면 해당 컴퓨터환경에서 포트포워딩을 열어 80번 포트를 열어주거나 dmz를 열어주셔야됩니다.

  5. 잘모르겟어요 2014.11.22 15:38 address edit & del reply

    음 안드로이드와 php연동 연습중에있습니다. 이거 그대로 하면 버튼 누르면
    <!DOCTYPE HTML PUBLIC"-//IETF//DTD HTML 2.0//EN"><html><head><title>403Forbidden</title></head><body><h1>Forbidden</h1><p>You don't have permission to access/text.phpon(Win64)PHP/5.5.12 Server at 192.168.123.126 Port 80</address></body></html> 이렇게나오는데 무엇이 잘못된걸까요,,모르겠습니다.

  6. asd 2015.01.27 14:39 address edit & del reply

    이거 그대로 php파일만들어서 실행하는데
    바로 오류뜨고 강제종료되던데..되는소스맞아요 ??

  7. 잘봤습니다. 2015.01.28 11:07 address edit & del reply

    프로그램 실행하자마자 오류로 인해 종료되었꼬 logcat을 보니 이렇게 떳는데 이유가무었을까요 ?
    블로그 글과 똑같이 작성했습니다...ㅠㅠㅠ

    01-28 10:59:17.836: E/(10924): Device driver API match
    01-28 10:59:17.836: E/(10924): Device driver API version: 23
    01-28 10:59:17.836: E/(10924): User space API version: 23
    01-28 10:59:17.836: E/(10924): mali: REVISION=Linux-r3p2-01rel3 BUILD_DATE=Fri Nov 29 14:18:37 KST 2013
    01-28 10:59:17.901: D/OpenGLRenderer(10924): Enabling debug mode 0
    01-28 10:59:29.356: W/dalvikvm(10924): threadid=12: thread exiting with uncaught exception (group=0x42397700)
    01-28 10:59:29.356: E/AndroidRuntime(10924): FATAL EXCEPTION: AsyncTask #1
    01-28 10:59:29.356: E/AndroidRuntime(10924): java.lang.RuntimeException: An error occured while executing doInBackground()
    01-28 10:59:29.356: E/AndroidRuntime(10924): at android.os.AsyncTask$3.done(AsyncTask.java:299)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.util.concurrent.FutureTask.run(FutureTask.java:239)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.lang.Thread.run(Thread.java:841)
    01-28 10:59:29.356: E/AndroidRuntime(10924): Caused by: java.lang.IllegalStateException: Target host must not be null, or set in parameters. scheme=null, host=null, path=susemi.pe.kr/test/index.php
    01-28 10:59:29.356: E/AndroidRuntime(10924): at org.apache.http.impl.client.DefaultRequestDirector.determineRoute(DefaultRequestDirector.java:591)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:293)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:670)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:509)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at com.example.dbtest3.MainActivity$HttpTask.doInBackground(MainActivity.java:73)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at com.example.dbtest3.MainActivity$HttpTask.doInBackground(MainActivity.java:1)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at android.os.AsyncTask$2.call(AsyncTask.java:287)
    01-28 10:59:29.356: E/AndroidRuntime(10924): at java.util.concurrent.FutureTask.run(FutureTask.java:234)
    01-28 10:59:29.356: E/AndroidRuntime(10924): ... 4 more
    01-28 10:59:31.646: I/Process(10924): Sending signal. PID: 10924 SIG: 9