반응형

JOIN은 각 테이블간에 공통 조건으로 데이터를 연결하여 표현하는 것입니다.

기본 4가지 Join방법에 대해 도식과 함께 기억하면 쉽게 이해하고 현업에서 사용할 수 있을 것입니다.

 

 

1) 기본 조인: 공통 속성이 있는 리스트만 추출

 

 

 

SELECT A.field1, A.field2, B.field3

FROM   Table_A, Table_B

WHERE A.ID = B.ID;

 

 

 

 

 

2) LEFT OUTER JOIN: A 테이블은 모두 나오고, B테이블에 공통값이 없는 경우 NULL값 출력

 

 

SELECT A.field1, A.field2, B.field3

FROM   Table_A, Table_B

WHERE A.ID = B.ID(+); //값이 없을수도 있는쪽에 (+)연산자를 붙인다.

 

SELECT A.field1, A.field2, B.field3

FROM   Table_A LEFT OUTER JOIN Table_B

on A.ID = B.ID;

 

 

 

3) RIGHT OUTER JOIN: B 테이블은 모두 나오고, A테이블에 공통값이 없는 경우 NULL값 출력

 

 

SELECT A.field1, A.field2, B.field3

FROM   Table_A, Table_B

WHERE A.ID(+) = B.ID; //값이 없을수도 있는쪽에 (+)연산자를 붙인다.

 

SELECT A.field1, A.field2, B.field3

FROM   Table_A RIGHT OUTER JOIN Table_B

on A.ID = B.ID;

 

 

 

4) FULL OUTER JOIN: 상대 테이블에 공통값이 없는 경우 NULL값 출력

 

 

 

SELECT A.field1, A.field2, B.field3

FROM   Table_A FULL OUTER JOIN Table_B

on A.ID = B.ID;

 

 

 

 

 

그 외에도 몇가지 조인방법이 있긴 한데, 기본 개념을 이해하면 쉽게 응용 가능한 부분이라 생략하도록 하겠습니다.

 

반응형
반응형

현재 내 리눅스머신 화면

저는 윈도우와 리눅스를 멀티부팅으로 사용합니다.

아무리 가상환경이 좋아졌다지만 각각의 OS를 사용하는 것만 못한것 같아서 둘 다 깔아놓고 씁니다.

물론 메인은 리눅스로, 회사일이나 은행일 볼때는 윈도우로(사실 모바일로 다 하지만) 로그인합니다.

 

서버용 리눅스는 잘 모르겠으나 데스크탑으로는 만자로도 개인적으로 만족하고 있습니다. 물론 심각한 개발을 한다거나 하진 않습니다. Godot엔진으로 간단한 게임 만들고, Visual Studio Code로 웹사이트 끄적거리고, IntelliJ로 아주 초보적인 JAVA 프로그래밍, LiteIDE로 아주 초보적인 Golang 스터디 정도 하고 있네요. 물론 인터넷, 토렌트, 음악은 예전부터 유명한 몇몇 프로그램이 있어서 잘 사용하고 있습니다.

 

개인적으로는 바탕화면에 프로그램 실행파일들이 널려있으면 지저분해져서 뭔가 정리하고 싶은 마음이 항상 들었는데,

가능은 하지만 귀찮아서 굳이 꺼내놓지 않게끔 하는 이 리눅스의 환경이 저한테는 깔끔하니 이뻐서 더 손이 가게 하네요.

 

항상 처음 설치할 때 한글 설정으로 버벅거린 경험과, C#을 하겠답시고 이것저것 깔아보다가 실패한 경험이 있지만, 주제넘치는 시도는 당분간 잠재우고, 일반적인 개발과 일반적인 인터넷, 일반적인 멀티미디어 활용으로도 아직은 할일이 넘치는 것 같습니다.  당분간 계속 만자로를 쓸 것 같습니다.

 

만자로 깔면서 매번 다른 블로그를 찾아가며 시행착오 겪었던 일들을 정리하는 시간을 갖도록 할 예정입니다.

 

최초 설치시 설치/설정 필요항목

1. ibus한글 설치

2. Guake, Plank 설치

3. 저장소 추가 및 Update

 

반응형
반응형

자바로 데이터베이스 연결 프로그램 구현 시, DB연결부 구현파일입니다.

 

package DB;
import java.sql.*;

public class DBConnection
{
  public static Connection dbConn;
  public static Connection getConnection() 
  {
    Connection conn = null;
    try{
        String user="abcd";
        String pw="********";
        String url="jdbc:oracle:thin:@zxcv.dothome.co.kr:1521:odbc";
        //이렇게 해야 연결이 된다. C#과 차이
        Class.forName("oracle.jdbc.driver.OracleDriver");
        System.out.println("driver load success");
        conn=DriverManager.getConnection(url,user,pw);
        System.out.println("DBConnectsuccess");
    }catch(ClassNotFoundExceptioncnfe){
        System.out.println("DB드라이버로딩실패:"+cnfe.toString());
    }catch(SQLExceptionsqle){
        System.out.println("DB접속실패:"+sqle.toString());
    }catch(Exceptione){
        System.out.println("Unkonwn error");
        e.printStackTrace();
    }
    return conn;
  }
}

 

반응형

'Programming > Java_Etc' 카테고리의 다른 글

AWT Study - 팝업메뉴  (0) 2020.02.11
AWT Study - 메뉴 생성  (0) 2020.02.11
AWT Study - Window & Frame  (0) 2020.02.11
Java 막대 그래프 그리기2  (0) 2020.02.08
Database Connection - OracleTest.java  (0) 2020.01.29
반응형

1. DBConnection.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
public class DBConnection 
{
    public static Connection dbConn;
    
    public static Connection getConnection()
    {
      Connection conn = null;
      try {
        String user = "scott"; 
        String pw = "tiger";
        String url = "jdbc:oracle:thin:@localhost:1521:orcl";

        Class.forName("oracle.jdbc.driver.OracleDriver");        
        conn = DriverManager.getConnection(url, user, pw);

        System.out.println("Database에 연결되었습니다.\n");

      } catch (ClassNotFoundException cnfe) {
          System.out.println("DB 드라이버 로딩 실패 :"+cnfe.toString());
      } catch (SQLException sqle) {
          System.out.println("DB 접속실패 : "+sqle.toString());
      } catch (Exception e) {
          System.out.println("Unkonwn error");
          e.printStackTrace();
      }
    return conn;     
    }
}

 

2. DB 데이터추출 구현부

package DB;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class OracleTest
{
    public static void main(String args[])
    {
        Connection conn = null; // DB연결된 상태(세션)을 담은 객체
        PreparedStatement pstm = null; // SQL 문을 나타내는 객체
        ResultSet rs = null; // 쿼리문을 날린것에 대한 반환값을 담을 객체
        
        try {
          // SQL 문장을 만들고 만약 문장이 질의어(SELECT문)라면
          // 그 결과를 담을 ResulSet 객체를 준비한 후 실행시킨다.
          String quary = "SELECT * FROM (select * from TEMP order by empno) where rownum<=10";
          //String quary = "SELECT * FROM (select * from TEMP order by EMP_#) fetch first 100 rows only";
          conn = DBConnection.getConnection();
          pstm = conn.prepareStatement(quary);
          rs = pstm.executeQuery();
          System.out.println("EMPNO EMP_X KoreanName HanmunName CommonNumber");
          System.out.println("============================================");

          while(rs.next()){
              String empno = rs.getString(1);
              int emp_x = rs.getInt(2);
              String Korean_name = rs.getString(3);
              String Han_name = rs.getString(4);
              int comm = rs.getInt("RES_#1");
              //java.sql.Date hiredate = rs.getDate(5); // Date 타입 처리
              String result = empno+" "+emp_x+" "+Korean_name+" "+Han_name+" "+comm;
              //                      직원번호       구분           한글이름             한문이름        주민번호
              System.out.println(result);
           }
           
        } catch (SQLException sqle) {
        	System.out.println("SELECT문에서 예외 발생");
        	sqle.printStackTrace();
            
        }finally{
        // DB 연결을 종료한다.
            try{
                if ( rs != null ){rs.close();}
                if ( pstm != null ){pstm.close();}
                if ( conn != null ){conn.close(); }
                
            }catch(Exception e){
            	throw new RuntimeException(e.getMessage());
            }
        }
    }
}

 

반응형

'Programming > Java_Etc' 카테고리의 다른 글

AWT Study - 팝업메뉴  (0) 2020.02.11
AWT Study - 메뉴 생성  (0) 2020.02.11
AWT Study - Window & Frame  (0) 2020.02.11
Java 막대 그래프 그리기2  (0) 2020.02.08
Database Connection - DBConnection.java  (0) 2020.01.29
반응형

1. Title screen제작

  - User Interface(Control) 추가하고 이름을 MainScreen으로 변경합니다.


  - Background 추가 후 TextureRectangle로 변경, 

  - Layout: Full Rect

  - 인스펙터에서 Expand: on, Stretch Mode: Tile 로 설정


  - MainScreen Node에 Label 노드 추가(Title)
    ○ My First Godot Game : Text 변환
    ○ Center Top: Layer 변경, Shift 누른채로 약간 아래로 변경


   - VBoxContainer추가하여 이름을 Menu로 변경하고, 아래와 같이 버튼2개를 추가합니다.
    ○ Layout: Center
    ○ Button 2개 추가 및 텍스트 변경

    ○ Button을 VBoxContainer에 끌어다 놓음
    ○ Box 사이즈 조절
    ○ Button 2개 선택 후 size flags: expand vertically


  - Title/Menu/PlayButton/QuitButton으로 각 요소의 이름을 변경하고,  src/UserInterface폴더를 생성하여 별도 scene으로 각각 저장합니다.


  - Scene저장(res://src/Screens/MainScreen.tscn으로 저장

 

  - Project > Project Settings > Run 메뉴에서 Main Scene을 기존 Level1에서 MainScreen.tscn으로 변경해줍니다. 즉, 직접 레벨이 실행되던것을 UI화면이 뜨고, Play를 누르면 Level1으로 진입하도록 합니다.

 

2. 테마 설정

Title - font에 폰트 추가 대신 Theme기능을 이용하여 Scene 전체적으로 스타일을 적용하는 과정입니다. (제공되는 테마를 사용할 경우 skip 가능)
  - Dynamic Font 추가

      ○ MainScreen노드에서 Add Resource Button을 클릭, Dynamic Font노드를 추가합니다.


    - Font에 font file추가


    - size: 24로 변경
    - Assets 폴더에 new_dynamicfont.tres로 추가


  - Add Resource Button을 한번 더 클릭합니다.
  - Theme 노드 추가


    - new_dynamicfont.tres를 Default Font에 추가


    - Assets 폴더에 ui_theme.tres로 저장


  - MainScreen에서 Theme에 ui_theme.tres를 추가


  - Title.tscn을 열고 Text를 일반적으로 쓸 수 있도록 Title로 바꿔줍니다.

     ○ Align: center
     ○ Font: font_title.tres를 추가해줍니다.


  - 다시 MainScreen으로 와서 top center: 위치 조정

 3. Play / Quit Button에 Script추가

  - PlayButton.tscn을 열고, PlayButton노드에서 새 스크립트 추가를 하여 PlayButton.gd로 이름을 변경합니다.

그리고 Signal: button_up 을 추가합니다.(_on_button_up() 함수 생성)


  - Button이름을 ChangeSceneButton으로 변경합니다.(근데, 스크립트 이름은 안바꾸네요..^^;;)


<PlayButton.gd>

tool #플러그인 사용
extends Button

export(String, FILE) var next_scene_path:="" #string형태로 scene을 받아들이고, 파일 다이얼로그를 보여줌

func _on_button_up():
	get_tree().change_scene(next_scene_path)

func _get_configuration.warning() -> String:
	return "next_screne_path must be set for the button to work" if next_scene_path=="" else ""

  - QuitButton-Script추가


  - Node탭-button_up시그널 추가(_on_button_up)


<QuitButton.gd>

extends Button

func _on_button_up():
	get_tree().quit()

 

4.EndScreen생성

  - new scene-userinterface - EndScreen.tscn저장(screen폴더)

 

  - Merge from scene - mainscreen.tscn에서 textureRect 선택으로 배경 불러오기


  - Merge from scene - mainscreen.tscn에서 Menu 선택으로 메뉴/버튼 불러오기
     ○ Next scene에 mainscreen을 선택
     ○ Play버튼 텍스트를 Play Again으로 변경


  - EndScreen선택 후 resource에서 title검색으로 title.tscn을 가져와서 화면으로 drag & drop
     ○ Title text변경: Congratulations, you finished the game
     ○ Layout: center top
     ○ 아래쪽으로 위치이동


  - Label 노드 추가
     ○Layout: center top
     ○Text수정: 텍스트 오른쪽의  expand button    

your final score is %s
you died times %s

    - 헌번 더 layout 조정, 텍스트 얼라인 center조정


  - Title, Label, Menu를 동시 선택 후 layout - center로 중앙 정렬 후 위치 조정
     ○ 화면 사이즈가 변하더라도 위치변화로 이미지가 겹치지 않는다.

 

5. Score 시스템

  - new scene - Node추가 (Autoload폴더에 PlayerData.tscen으로 저장)
  - Script 생성 - PlayerData.gd로 저장

<PlayerData.gd>

extends Node

signal score_updated
signal player_died

var score: = 0 setget set_score #매 score변경시마다 setter함수 호출
var deaths: = 0 setget set_deaths

func reset() -> void:
	score = 0
	deaths = 0

# 다른 스크립트에서 score가 변경되는 이벤트가 발생하여 +1이 되면 setget으로 set_score함수가 실행되고,
score변수에 +1된 값을 설정함과 동시에 "score_update" 시그널을 보낸다.

func set_score(value: int) -> void:
	score = value
	emit_signal("score_updated")

func set_deaths(value: int) -> void:
	deaths = value
	emit_signal("player_died")


  - Project Settings - autoload -PlayerData.tscn - ADD


  - 스크립트 수정

<Player.gd>

func _on_EnemyDetector_body_entered(body: PhysicsBody2D)->void:
	#queue_free() 수정/제거
    die() #추가 함수 생성

func die()-> void:	#신규함수
	PlayerData.deaths += 1
	queue_free()

<Enemy.gd script 수정>

export var score: = 100

func die() -> void:
	queue_free()
	PlayerData.score += score	# 추가

 

<Coin.gd script 수정>

export var score: = 100

func picked() ->void:
	PlayerData.score += score 	#추가
	anim_player.play("picked")

 

6. UI생성

  - User Interface 추가 - UserInterface로 변경 후 UserInterface폴더에 저장
  - ui_theme.tres를 theme에 추가


  - Label 추가(score용) - layout - top right - text align: right

Score %s


  - Color Rect 노드 추가(PauseOverlay로 이름 변경)
     ○ 색상 black, alpha:63정도로 변경

  - title.tscn을 끌어다 추가, text: Paused  - VBoxContainer추가,  layout center, alt키와 동시에 눌러 사이즈 조정, PauseMenu로 이름 조정  - QuitButton.tscen, SceneChangeButton.tscn을 선택하여 VBox안으로 가져옴(Ctrl+방향키로 버튼위치 변경 가능)


  - SceneChangeButton의 Next Scene을 MainScreen.tscn으로 설정(우클릭-path copy기능으로)


  - SceneChangeButton을 복제, RetryButton으로 변경후 더블클릭(Text: Retry로 변경)


  - Script 제거 후 재 추가


<RetryButton.gd scrip 추가>

Extends Button

func _on_button_up() -> void:
	PlayerData.score = 0
	get_tree().paused = false
	get_tree().get_root().get_node("/root/UserInterface/PauseOverlay").visible = false
	get_tree().reload_current_scene()


  - PauseOverlay visibility를 OFF


  - UserInterface.gd script 작성 

<UserInterface.gd script>

extends Control

onready var scene_tree: = get_tree()  #_ready함수 전에 실행됨
onready var pause_overlay: ColorRect= get_node("PauseOverlay") #ColorRect type임을 명시해줌. 혼선의 여지가 없을 경우는 선언 안하더라도 상관 없음.

var paused: = false setget set_paused

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("pause"):
	self.paused = !paused #(== "paused = not paused") #Self는 gdscript에서 기본이지만, setget함수를 위해서...
	scene_tree.set_input_as_handled() #pause event가 끝날때까지 propagate하지 않고 기다린다.


func set_paused(value: bool) ->void:
	paused = value
	scene_tree.paused = value

  - Project Settings - Input Map - pause생성 - Key 추가(+버튼) - Esc로 설정
  - UserInterface.tscn을 Level01에 추가
     ○ Canvaslayer추가(UserInterface로 변경)
     ○ 상위 노드로 위치를 바꾸고 Layer를 +100정도로 앞쪽으로 한다.
     ○ 그 후 Canvaslayer에 UserInterface.tscn을 하위 자식으로 추가


  -  UserInterface노드 선택 후 Node-Pause-Process로 선택(Inherit(default)/Stop/Process 중)
     ○ 기본으로 되어있는 Inherit는 상위노드를 상속하고 상위노드는 Stop으로 되어있다.
     ○ Stop은 완전 정지로 아무것도 Process할 수 없는 상태임


  - 추가로, UserInterface 씬에서 Label을 hide하고, 각 level에서 인스턴스된 UserInterface 노드에서는 Label을 show해야만 Label이 이중으로 나타나는 현상이 사라진다.

<UserInterface.gd script>

extends Control

onready var scene_tree: = get_tree()  #_ready함수 전에 실행됨
onready var pause_overlay: ColorRect= get_node("PauseOverlay") #ColorRect type임을 명시해줌. 혼선의 여지가 없을 경우는 선언 안하더라도 상관 없음.
onready var score: Label = get_node("Label")
onready var pause_title: Label = get_node("PuaseOverlay/Title")

const DIED_MESSAGE: = "You died"

var paused: = false setget set_paused

func _ready() -> void: #46
	PlayerData.connect("score_updated", self, "update_interface")
	PlayerData.connect("player_died", self, "_on_PlayerData_player_died")
	update_interface()

func _on_PlayerData_player_died() -> void:
	self.paused = true
	pause_title.text = DIED_MESSAGE

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("pause") and pause_title.text != DIED_MESSAGE: #죽었을 때 esc를 누르면 계속 진행되는 현상 방지
	self.paused = !paused #(== "paused = not paused") #Self는 gdscript에서 기본이지만, setget함수를 위해서...
	scene_tree.set_input_as_handled() #pause event가 끝날때까지 propagate하지 않고 기다린다.

func update_interface() -> void:
	score.text = "Score: %s" % PlayerData.score

func set_paused(value: bool) ->void:
	paused = value
	scene_tree.paused = value

 

7. UI씬을 각 level에 복사


  -  level02 열기
     ○ Merge from scene
     ○ level01의 UserInterface(Canvaslayer) 선택
  - Level template에도 적용

 

8. Endscreen 수정

  -  Endscreen 노드에 script 추가

<Endscreen.gd>

extends Control

onready var label: Label = get_Node("Label")

func _ready() -> void:
	label.text = label.text % [PlayerData.score, PlayerData.deaths]


  - Level02의 nextscene에 Endscreen을 추가


  - Project Settings - windows - Fullscreen으로 변경하고 Play

 

---- 끝 ---

반응형
반응형

1. Coin 만들기

  - new scene > Area2D(Coin) 노드추가, Coin Image추가


  - CollisionShape2D추가, circle shape 선택 후 사이즈 조정합니다. 


  - AnimationPlayer추가 (Objects폴더 생성 후 scene저장)


  - animation - new animation: bouncing


  - timeline 오른쪽에 시간을 1.8로 전체 애니메이션 시간을 설정
  - timeline 오른쪽의 반복기능 on


  - 뷰포트의 rotation mask for inserting keys를 언체크함
  - 뷰포트의 Translation mask for inserting keys만 체크함


  - Coin 노드 선택 후 ins키나 뷰포트의 key를 누르면 애니메이션에 key가 추가됨

  - 0.9초에 key frame추가 후 y 값을 -10으로 설정
  - 첫 key frame의 y값을 +10으로 설정


  - 첫 프레임의 easing 진동하는 속도...를 상향 조정한다.
  - 두번째 프레임의 easing 을 하향 조정한다.


  - animation time을 1.2로 수정하여 짧게 변경하고, 두번째 프레임의 위치도 중간인 0.6정도로 위치를 변경합니다.


  - animation - new animation: fade_out 애니메이션을 추가합니다.


  - Coin(Area2D)선택
  - transform에서 원점인지 확인 후 작업을 진행합니다.


  - canvasItem > visibility > modulate 오른쪽의 key를 클릭하여 0에서, 0.6초에서 프레임을 추가하면  Modulate속성이 애니메이션에 추가됩니다.


  - 0.6초로 key frame이동 후 Modulate 오른쪽의 key를 클릭 후 alpha를 0으로 수정


  - animation 시간을 0.6초로 변경
  - shift+D로 프리뷰 가능

  - bouncing에서 첫 프레임에 modulate에서 key 추가하여 alpha 100%를 추가


  - fade_out에서 마지막 프레임에 add track버튼 - call method track 버튼 - method 추가


  - coin(Area2D)노드 선택 후 프레임의 오른쪽클릭 후 insert method 클릭

  - queue free()선택

  - Coin 스크립트 추가

<coin.gd>

extends Area2D

onready var anim_player: AnimationPlayer = get_node("AnimationPlayer")
#_ready 실행하기 전에 실행

func _on_body_entered(body:PhysicsBody2D) ->void:
	anim_player.play("fade_out")

 

  - Coin에서 layer는 coin, Mask는 player로 선택

  - signal 추가: body_entered(PhysicsBody2D body) 


  - bouncing 클릭 후 오른쪽의 버튼을 누르면 기본 animation으로 선택됨

 


  - 여기까지 따라하면 코인의 움직임이 안보입니다. animation이 Coin(Area2D)에 설정되어있어서 Play를 시작하면 animation이 원점에서 실행되고 있습니다. 이를 방지하려면 coin(sprite) 노드가 애니메이션되도록 해야합니다. 코인의 AnimationPlayer노드로 가서 ".:position" 으로되어있는 부분을 "coin:position"으로 바꿔줍니다.

  - fade_out 에니메이션을 0.4초로 변경

 

2. Portal 생성

 

  - Portal2D(Area2D)노드를 추가하고 Portal 이미지를 추가합니다.


  - CollisionShape2D 추가
  - CapsuleShape 설정


  - TransitionLayer(canvaslayer) 노드를 추가해주고, 다른 어떤 것보다도 앞에 있어야하므로 Layer 값을 +100 정도로 설정해줍니다.


  - TransitionLayer에 ColorRect 노드를 추가하여 Black으로 색깔 변경하고(layout-Full rect)하고 alpha는 0으로하여 기본적으로는 안보이게 설정합니다.


  - AnimationPlayer 추가
     ○ start애니메이션 추가. 시작될 때 검은 화면이 다시 투명하게 바꾸는 역할을 하는 애니메이션입니다.

     ○ autoplay 체크

     ○ ColorRect선택-color key 지정(투명, 처음엔 안나타나게)
     ○ Visible: off(value: off)로 선택하고 key 지정

     ○ 지속시간은 0으로 하여 한번 투명하게 값을 설정하고 끝내도록 합니다.

Visibility off인지 한번 더 확인


     ○ fade_in애니메이션 생성 (START애니메이션 복제(Ctrl+D))

     Q Visibility는 On

     ○ 지속시간 1초

     ○ 1초에 key frame 추가 - color 선택에서 alpha 255%(블랙)으로 설정 후 키 추가


  - Portal2D Monitoring: on, Monitorable: off, Layer: none, Mask: player 로 설정

3. Portal 스크립트

<protal2D.gd>

tool
extends Area2D

onready var anim_player: animationPlayer = $AnimationPlayer

export var next_scene: PackedScene

func _on_body_entered(body: PhysicsBody2D) -> void:
	teleport()

func _get_configuration_warning() -> String:
	return "The next scene property can't be empty" if not next_scene else ""

func teleport() ->void:
	anim_player.play("fade_in")
	yield(anim_player, "animation_finished") #anim_player가 끝나서 animation_finished 시그널을 보낼 때까지 기다림
	get_tree().change_scene_to(next_scene)
 

  - nextscene을 export로 추가하고, 없으면 에러메세지 popup띄우는 기능 추가

  - teleport함수 추가
  - body_entered(PhysicsBody2D body) 시그널 연결 : _on_body_entered
  - Level을 우선 복제하여 테스트

#참고로...터치패드에서 대량으로 tilemap을 지울 때에는 우클릭에 해당하는 두손가락 클릭을 하는데,,,한번하면 생성, 두번하면 삭제
#한손가락은 패드도 눌러야하고, 검지손가락은 터치도 해야함...두번씩....

반응형
반응형

 

따라해야할 양이 많다보니 포스팅이 친절하지 못한점 양해바랍니다.^^;;

그럼 시작하겠습니다.


1. 프로젝트 세팅

  - assets 다운로드(예제 소스 링크)

  - platformer폴더 생성 후 assets폴더를 복사해 넣습니다. 
  - assets 폴더에 이미지 등 소스 추가
  - src 폴더 추가

2. Player 생성

  - kinematicbody2D 추가

  - Player로 변경
  - CollisionShape2D추가 ->Rectangle shape 설정
  - Player 이미지를 끌어오면 자동으로 sprite node가 추가되며 이름은 player로 설정됨
  - magnetic을 클릭하여 snap 활성화
  - pixel snap을 활성화하여 맨 바닥이 0에 오도록 설정
  - Collisionshape2D의 크기 조정(캐릭터 이미지보다 약간 작도록 설정, 단 바닥은 0에 닿도록 함) 
  - Player Scene을 src/Actors폴더 안에 저장
  - Player node에 script 추가(Empty template로 설정하여 추가)
  - File menu에서 script 추가하여 Actor.gd(palyer와 enemy가 공유할 base script)를 추가 (player.gd는 현 단계에서는 필요 없음.

------Actor.gd script 1단계-------

extends KinematicBody2D
class_name Actor

const FLOOR_NORMAL: = Vector2.UP #플로어에 있는지에 관한 정보

export var speed: = Vector2(300.0, 1000.0)
export var gravity: = 3000.0

var velocity: = Vector2.ZERO

func _physics_process(delta:float) ->void: #매 프레임마다 수행
	velocity.y += gravity * delta
	#나중에 추가: velocity.y = max(velocity.y, speed.y) //if(velocity.y >speed.y){velocity.y=speed.y}
	velocity = move_and_slide(velocity) #Player 스크립트로 이동

3. Level 생성(2D Node: LevelTemplate) 및 저장

  - Tilemap 추가, Tileset 추가(미리 저장된 것 불러오기, 새로 생성하여 사용 가능)

스텝 사이즈 80으로 설정
Collision지정으로 충돌/정지할 수 있도록 인식영역 설정
셀 사이즈를 타일셋에서 지정한 이미지 사이즈와 동일하게 80으로 설정


  - wall, floor 생성. 적당히 생성 후 Player를 불러와서 실행해본다.

4. 레이서 설정(Editor > Project Settings > 2D Physics)

- 설정의 2d physics 로 가서 Level 설정 추가(어떤 레벨과 반응하는지..충돌 등, 씬 안에있는 instance의 세팅을 변경할 경우 해당 instance만 변경되므로, 해당 씬을 수정해야함)

- Layer Names - 2D Physics
- Layer1: player
- Layer2: enemies
- Layer3: coins
- Layer4: world
-Tilemap: Layer-world, Mask-none
-Player: 

 

예를 들면 TileMap같은 경우에는 Collision > Layer 메뉴의 ... 버튼에서 어떤 레이어인지 직접 지정해줄 수 있다. (마스크는 제외해줘야 함)

 

Player는 우선 4번 (world)를 마스크해서 감지하도록 해야함.

 

5. Project Setting - Input Map

-move_left (A, Left, Devide 0(Stick Left)
-move_right(D, Right, Device 0(Stick Right)
-jump(W, Up, Device 0(PS Cross, xBox A nintendo B)


--------Player.gd script----------
- 1단계 : 키 인식 확인용

extends Actor

func _physics_process(delta: float) -> void:
	var direction: = Vector2(
		Input.get_action_strength("move_right") -
		Input.get_action_strength("move_left"),
		1.0
	)
	velocity = speed * direction
	velocity = move_and_slide(velocity)

 

- 최종 -

extends Actor

export var stomp_impulse: = 1000

func _on_EnemyDetector_area_entered(area: Area2D) ->void:
	_velocity = calculate_stomp_velocity(_velocity, stomp_impulse) #enemy를 밟을때 충격으로 튀어오르는 기능

func _on_EnemyDetector_body_entered(body: PhysicsBody2D) -> void: #enemy와 부딛혔을 때 죽는 기능
	queue_free()

#1단계
func _physics_process(delta: float)->void:
	var is_jump_interrupted: = Input.is_action_just_released("jump") and velocity.y <0.0 #현재 점프상태가 끝나면 jump도 짧게 끝남. 키 누르는 시간에 비례하도록)
	direction: = get_direction()
	velocity = calculate_move_velocity(velocity, direction, speed, is_jump_interrupted) 
	velocity = move_and_slide(velocity, FLOOR_NORMAL) #현재 player가 frame 밖에 있으므로 안쪽으로 옮겨준 뒤 실행가능


#1단계
func get_direction() -> Vector2:
	return Vector2(
		Input.get_action_strength("move_right")-Input.get_action_strength("move_left"), -1.0 if Input.is_action_just_pressed("jump") and is_on_floor() else 1.0) #벽에서 점프한 경우에만 y 방향을 -1로 설정하고, 그 외에는 1로 설정함

#1단계
func calculate_move_velocity(
      linear_velocity: Vector2,
      direction: Vector2,
      speed: Vector2
      is_jump_interrupted: bool
      ) ->Vector2:
	var new_velocity: = linear_velocity
	new_velocity.x = speed.x * direction.x
	new_velocity.y += gravity * get_physics_process_delta_time() //delta값을 호출해줌
	if direction.y == -1.0:
		new_velocity.y = speed.y * direction.y
	if is_jump_interrupted:
		new_velocity.y = 0.0
	return new_velocity

func calculate_stomp_velocity(linear_velocity: vector2, impulse: float) -> Vector2:
	var out: = linear_velocity
	out.y = -impulse
	return out

  - player와 enemy속도 차이를  위해 player 600 설정
  -  Jump 함수 추가(방향함수를 추가하여 가독성 향상)
  - Jump 함수에 FLOOR_NORMAL 상수를 추가로 전달하여야 JUMP가 가능함
  - new_velocity 변수 일괄 변경: 선택-> Ctrl+R(대체)->out
  - Ctrl+Shift+F: 파일 전체(전체프로젝트)에서 찾기: velocity찾기->whole word체크(전체일치)->replace->_velocity 로 변경(private:해당 script/class에서만 쓸 수 있음)

6. Enemy

  - Player.tscn -> duplicate: 이름 변경 to Enemy, sprite 이름 enemy
  - enemy.png를 sprite texture에 놓는다.
  - Collisionshape2D  사이즈 변경

  - extend script - actor.gd - enemy.gd생성

Actor.gd를 상속하고, 이름은 Enemy.gd로 하여 생성

- layer를 enemy layer로, mask를 player/world로


<enemy.gd 스크립트>

extends "res://src/Actors/Actor.gd"

func _ready() ->void:
	set_physices_process(false) #enemy가 밖에 있을 때 작동하지 않도록 하는 기능
	_velocity.x = -speed.x 

func _on_StompDetector_body_entered(body:
		PhysicsBody2D) ->void:
	if body.global_positioy.y < get_node("StompDetector").global_position.y: 
		return
	get_node("CollisionShape2D").disabled = true # 죽이고도 내가 또 죽는 오류를 방지
	queue_free()	# 적 삭제

func _physics_process(delta: float) ->void: #1단계
	_velocity.y +=gravity * delta
	if is_on_wall(): #1단계
		_velocity.x *= -1.0 #1단계
		_velocity.y = move_and_slice(_velocity, FLOOR_NORMAL).y #1단계

  - Frame 밖에 있을 때는 작동하지 않도록 하는 기능 ( 노드 추가: VisibilityEnabler2D )

  - physics Process Par: on,  process parent: on

- "physics Process Par: on" 옵션은 화면 밖으로 나가는 것에대해서만 Trigger역할을 합니다. 즉, 이미 오른쪽 밖에서 안으로 이동하는 녀석에 대해서는 작동하지 않습니다. 계속 왼쪽 (화면 안쪽)으로 이동합니다. 따라서 추가 함수가 필요합니다.

func _ready() ->void:
	set_physices_process(false) #enemy가 이미 밖에 있을 때도 작동하지 않도록 하는 기능
	_velocity.x = -speed.x


  - Frame Trigger box 사이즈/위치 조절(-50, -50, 100, 60) 


  - Enemy에 Area2D 노드 추가(StompDetector로 이름 변경)


  - Layer 체크 삭제
  - Monitorable: off


  - CollisionShape2D 노드 추가: rectangleshape/사이즈 조정


  - StompDetector위치는 위로 올리고, 아래에서부터 부딛힐 때도 죽으면 안되니까..

  - 내부의 CollisionShape2D는 아래로 내려서 위치를 조정한다. 

  -  박스 색깔을 조절한다.



  - signal연결: body_entered(PhysicsBody2d)


7. 적을 밟아 제거하면 튀어오르는 모션 추가

이제 적당히 움직임이 구현되었습니다. 거기에 더해 Player 동작에 적을 밟았을 때 튀어오르는 모션을 추가해보겠습니다.

  - player에 Area2D 노드 추가(EnemyDetector로 이름 변경)


  - Layer: 없음, Mask: enemy
  - Monitorable: off


  - collisionshape2D - rectangleshape추가
  - 사이즈 조절

살짝 크게, 아래쪽으로 살짝 더 내려오게 박스 설정

  - visibility tab에서 색깔 변경(파란색으로)

  - 적 제거후 튀어오르는 애니메이션 구현 코드


8. 적과 충돌시 Player 죽는 기능 구현

  - signal 연결: body_entered(PhysicsBody2D body) 적에 닿으면 죽는 기능 구현

  _on_EnemyDetector_body_entered(body: PhysicsBody2D) 함수 추가

 

9. 적당한 시점에 Level TileMap을 수정해준다.

 

10. 카메라

  - Player 노드에 Camera2D 추가
  - current: on으로 설정해야 카메라 뷰로 보임


  - w키로 프레임 위치 상향 조정

  - 카메라 이동 제한
  - player - camera2D -limit의 값 수정(left: 0, top: 0)
  - Drag Margin: 카메라 이동 없이 player가 움직일 수 있는 공간, left 0, right 0


  - Drag Margin H/V Enabled
  - Smoothing - Enabled:on, Speed:2로 카메라가 따라오는 속도를 낮춤.. 7정도로 유지


  - player-right click - Editable Children - Limit Right:3600으로 해당 레벨에서만 리밋 설정 가능


11. 배경 이미지 설정

  - backgraound 이미지를 Texture Rectangle로 화면 안쪽으로 끌고 들어옵니다. (TextureRect노드안에 설정) 

  - Layout: Full Rect정
  - 맨 위로 올리면 제일 뒤쪽에 보이도록 할 수 있다.


  - CanvasLayer 추가후 background 를 자식으로 추가하면, 일일이 배경을 복사해서 추가하지 않아도, 게임 진행하면서 일관되게 배경을 추력해줍니다.
  - layer에 -100으로 설정



  - visible Collision Shapes 체크를 끔으로써 게임에서 Collision shape들이 안보이게 하여 마무리 해 줍니다.

반응형
반응형

흔히 슈퍼마리오 게임으로 익숙하게 알고있는 횡 스크롤 형태의 게임을 Platformer 라고 하는 것 같습니다. Godot 엔진을 이용한 Platformer 게임만들기를 시작하려고 하는데요, youtube강좌 https://www.youtube.com/watch?v=Mc13Z2gboEk 를 따라해보았습니다. 총 3편에 걸쳐 진행하며 Godot 엔진에 익숙해질 시간을 가져보겠습니다.

 

아래는 예전에 핸드폰에 넣으려고 만들었던 게임의 스크린 샷인데, 지금은 소스가 안남아있네요..ㅠㅠ

어쨌든 시작해보겠습니다.

platformer 기초 강좌 프로젝트를 이미지 변경하고, 모바일용으로 변경

 

반응형

+ Recent posts