public void restart() { this.is_ended = false; this.t = -1.0f; this.distance = 0.0f; this.cv = new ControlVertex(); }
// カーブ上の点を求める. public ControlVertex calcVertex(float t) { ControlVertex cv = null; int segment_index = Mathf.FloorToInt(t); if (segment_index < 0) { cv = this.cvs[0]; } else if (this.cvs.Count - 1 <= segment_index) { cv = this.cvs[this.cvs.Count - 1]; } else { float[] pos_k = new float[4]; float[] tan_k = new float[4]; ControlVertex cv0 = this.cvs[segment_index]; ControlVertex cv1 = this.cvs[segment_index + 1]; float local_param = t - (float)segment_index; Curve.calc_konst(pos_k, tan_k, local_param); cv = Curve.lerp(cv0, cv1, pos_k, tan_k); } return(cv); }
// カーブの総道のりを計算する. public float calcTotalDistance() { float distance = 0.0f; ControlVertex cv_prev = this.calcVertex(0.0f); float t = 0.0f; float dt = 0.1f; float t_max = (float)(this.cvs.Count - 1); int safe_count = Mathf.CeilToInt(t_max / dt) + 1; for (int i = 0; i < safe_count; i++) { ControlVertex cv_current = this.calcVertex(t); distance += Vector3.Distance(cv_prev.position, cv_current.position); if (t >= t_max) { break; } t += dt; t = Mathf.Min(t, t_max); cv_prev = cv_current; } return(distance); }
// ================================================================ // // 区間を補間する. public static ControlVertex lerp(ControlVertex cv0, ControlVertex cv1, float[] pos_k, float[] tan_k) { ControlVertex dest = new ControlVertex(); dest.position = cv0.position * pos_k[0] + cv1.position * pos_k[1] + cv0.tangent * cv0.tension * pos_k[2] + cv1.tangent * cv1.tension * pos_k[3]; dest.tangent = cv0.position * tan_k[0] + cv1.position * tan_k[1] + cv0.tangent * cv0.tension * tan_k[2] + cv1.tangent * cv1.tension * tan_k[3]; return(dest); }
// ================================================================ // // 구간을 보간한다. public static ControlVertex lerp(ControlVertex cv0, ControlVertex cv1, float[] pos_k, float[] tan_k) { ControlVertex dest = new ControlVertex(); dest.position = cv0.position * pos_k[0] + cv1.position * pos_k[1] + cv0.tangent * cv0.tension * pos_k[2] + cv1.tangent * cv1.tension * pos_k[3]; dest.tangent = cv0.position * tan_k[0] + cv1.position * tan_k[1] + cv0.tangent * cv0.tension * tan_k[2] + cv1.tangent * cv1.tension * tan_k[3]; return (dest); }
public void appendCV(Vector3 position, Vector3 slope) { ControlVertex cv = new ControlVertex(); cv.index = this.cvs.Count; cv.position = position; cv.tension = slope.magnitude; cv.tangent = slope.normalized; this.cvs.Add(cv); }
// ================================================================ // // 制御点を更新する(自分の子供から). public void createControlVertices() { this.curve.cvs.Clear(); foreach (var i in System.Linq.Enumerable.Range(0, this.transform.childCount)) { GameObject child = this.transform.GetChild(i).gameObject; var cv = new SimpleSpline.ControlVertex(); cv.index = i; cv.position = child.transform.position; cv.tangent = child.transform.TransformDirection(Vector3.forward) * child.transform.localScale.z; cv.tension = 10.0f; this.curve.cvs.Add(cv); } }
// ================================================================ // // 제어점을 갱신합니다(자신의 자식에서부터). public void createControlVertices() { this.curve.cvs.Clear(); foreach(var i in System.Linq.Enumerable.Range(0, this.transform.childCount)) { GameObject child = this.transform.GetChild(i).gameObject; var cv = new SimpleSpline.ControlVertex(); cv.index = i; cv.position = child.transform.position; cv.tangent = child.transform.TransformDirection(Vector3.forward)*child.transform.localScale.z; cv.tension = 10.0f; this.curve.cvs.Add(cv); } }
// 현재 위치에서 나아간다(파라미터 지정). public void proceed(float dt) { if(!this.is_ended) { if(this.t < 0.0f) { this.t = 0.0f; } else { this.t += dt; } if(this.t >= this.curve.getEnd()) { this.t = this.curve.getEnd(); this.is_ended = true; } this.cv = this.curve.calcVertex(this.t); } }
// 現在位置から進む(パラメーター指定). public void proceed(float dt) { if (!this.is_ended) { if (this.t < 0.0f) { this.t = 0.0f; } else { this.t += dt; } if (this.t >= this.curve.getEnd()) { this.t = this.curve.getEnd(); this.is_ended = true; } this.cv = this.curve.calcVertex(this.t); } }
// 현재 위치에서 나아간다(현재 위치로부터의 거리). public void proceedByDistance(float dist) { do { if(this.is_ended) { break; } ControlVertex cv0; if(this.t < 0.0f) { this.t = 0.0f; } cv0 = this.curve.calcVertex(this.t); this.cv = cv0; // float dt = dist/this.curve.calcTotalDistance()*this.curve.getEnd(); float sdt = dt >= 0.0f ? 1.0f : -1.0f; dt = Mathf.Abs(dt); float dt0 = dt; float t0 = this.t; float advanced = 0.0f; float advanced0 = advanced; int i; // 최대 반복 횟수. int max_times = Mathf.CeilToInt(this.curve.getEnd()/dt); max_times = max_times >= 1 ? max_times : 1; for(i = 0;i < max_times;i++) { t0 = this.t; this.t += sdt*dt; if(this.t >= this.curve.getEnd() && sdt >= 0.0f) { this.t = this.curve.getEnd(); if(dt < dt0) { this.is_ended = true; } dt = Mathf.Max(0.0f, this.t - t0); } else if(this.t < 0.0f && sdt < 0.0f) { this.t = 0.0f; if(dt < dt0) { this.is_ended = true; } dt = Mathf.Max(0.0f, Mathf.Abs(this.t - t0)); } this.cv = this.curve.calcVertex(this.t); advanced0 = advanced; advanced += Vector3.Distance(cv0.position, this.cv.position); if(this.is_ended) { break; } if(advanced >= Mathf.Abs(dist)) { // 너무 지나친 경우. // 진행 거리를 반으로 나눠서 직전 장소부터 다시 함. dt *= 0.5f; if(dt < dt0/100.0f) { break; } this.t = t0; advanced = advanced0; } else { cv0 = this.cv; } } } while(false); }
public override void execute() { CameraControl camera = CameraControl.get(); // ---------------------------------------------------------------- // // 다음 상태로 이동할지 체크합니다. switch (this.step.do_transition()) { // 이벤트 시작. case STEP.START: { this.step.set_next(STEP.OPEN_DOOR); //camera.module.parallelMoveTo(this.get_locator_position("cam_loc_0")); Debug.Log("Name:" + this.player.controll.account_name); foreach (ItemManager.ItemState istate in GlobalParam.get().item_table.Values) { Debug.Log("Item:" + istate.item_id + " Own:" + istate.owner + " State:" + istate.state); if (istate.owner == this.player.controll.account_name && istate.state == ItemController.State.Picked) { // 이미 아이템을 획득했다면 가지고 갈 수 있게 합니다. ItemManager.get().activeItme(istate.item_id, true); ItemManager.get().finishGrowingItem(istate.item_id); QueryItemPick query = this.player.controll.cmdItemQueryPick(istate.item_id, false, true); if (query != null) { query.is_anon = true; query.set_done(true); query.set_success(true); } ItemManager.get().setVisible(istate.item_id, true); } } // 리모트에서 이사 중은 로컬도 이사합니다. do { MovingData moving = GlobalParam.get().remote_moving; if (!moving.moving) { break; } chrController remote = CharacterRoot.get().findCharacter(moving.characterId); if (remote == null) { break; } chrBehaviorNet remote_player = remote.behavior as chrBehaviorNet; if (remote_player == null) { break; } chrBehaviorNPC_House house = CharacterRoot.getInstance().findCharacter <chrBehaviorNPC_House>(moving.houseId); if (house == null) { break; } Debug.Log("House move event call:" + moving.characterId + ":" + moving.houseId); remote_player.beginHouseMove(house); // '이사중~' 말풍선 표시. house.startHouseMove(); } while(false); } break; // 배추가 옆으로 움직이고 & 대야를 타고 플레이어가 등장. case STEP.OPEN_DOOR: { if (this.hakusai_fcurve.isDone() && this.tarai_fcurve.isDone()) { this.step.set_next(STEP.GET_OFF_TARAI_0); } } break; // 대야에서 내립니다(기슭으로 점프). case STEP.GET_OFF_TARAI_0: { if (!this.player_jump.isMoving()) { this.step.set_next(STEP.GET_OFF_TARAI_1); } } break; // 대야에서 내립니다(조금 걷기). case STEP.GET_OFF_TARAI_1: { if (!this.player_move.isMoving()) { this.step.set_next(STEP.CLOSE_DOOR); } } break; // 배추가 돌아오고 & 대야가 밖으로 이동. case STEP.CLOSE_DOOR: { if (this.hakusai_fcurve.isDone() && this.tarai_fcurve.isDone()) { this.step.set_next(STEP.END); } } break; case STEP.END: { camera.module.popPosture(); this.step.set_next(STEP.IDLE); } break; } // ---------------------------------------------------------------- // // 상태가 전환됐을 때의 초기화. while (this.step.get_next() != STEP.NONE) { dbwin.console().print(this.step.ToString()); switch (this.step.do_initialize()) { // 이벤트 시작. case STEP.START: { camera.module.pushPosture(); this.player.beginOuterControll(); this.player.controll.cmdSetPosition(this.tarai_leave_spline.curve.cvs.back().position); this.tarai_fune.transform.position = this.tarai_leave_spline.curve.cvs.back().position; if (!this.is_local_player) { SoundManager.get().playSE(Sound.ID.SMN_JINGLE01); } } break; // 배추가 옆으로 이동하고 & 대야를 타고 플레이어가 등장. case STEP.OPEN_DOOR: { this.hakusai_set.setControl(true); this.hakusai_fcurve.setSlopeAngle(10.0f, 10.0f); this.hakusai_fcurve.setDuration(4.0f); this.hakusai_fcurve.start(); this.hakusai_tracer.restart(); this.tarai_fcurve.setSlopeAngle(60.0f, 5.0f); this.tarai_fcurve.setDuration(3.5f); this.tarai_fcurve.setDelay(0.5f); this.tarai_fcurve.start(); } break; // 대야에서 내립니다(기슭으로 점프). case STEP.GET_OFF_TARAI_0: { Vector3 start = this.player.controll.getPosition(); Vector3 goal = this.get_locator_position("chr_loc_0"); this.player_jump.start(start, goal, 1.0f); } break; // 대야에서 내립니다(조금 걷기). case STEP.GET_OFF_TARAI_1: { this.player_move.position.start = this.player.controll.getPosition(); this.player_move.position.goal = this.get_locator_position("chr_loc_1"); this.player_move.startConstantVelocity(chrBehaviorLocal.MOVE_SPEED); } break; // 배추가 돌아오고 & 대야가 밖으로 이동. case STEP.CLOSE_DOOR: { this.hakusai_fcurve.setSlopeAngle(10.0f, 10.0f); this.hakusai_fcurve.setDuration(4.0f); this.hakusai_fcurve.setDelay(1.0f); this.hakusai_fcurve.start(); this.hakusai_tracer.restart(); this.hakusai_tracer.setCurrentByDistance(this.hakusai_tracer.curve.calcTotalDistance()); this.tarai_tracer.attach(this.tarai_enter_spline.curve); this.tarai_tracer.restart(); this.tarai_fcurve.reset(); this.tarai_fcurve.setSlopeAngle(10.0f, 60.0f); this.tarai_fcurve.setDuration(2.5f); this.tarai_fcurve.start(); this.ripple_effect.is_created = false; } break; case STEP.END: { // 이벤트 종료. this.hakusai_set.reset(); this.hakusai_set.setControl(false); this.player.endOuterControll(); this.player = null; } break; } } // ---------------------------------------------------------------- // // 각 상태에서의 실행 처리. switch (this.step.do_execution(Time.deltaTime)) { // 배추가 옆으로 움직인다 & 대야를 타고 플레이어가 등장.. case STEP.OPEN_DOOR: { this.hakusai_fcurve.execute(Time.deltaTime); this.hakusai_tracer.proceedToDistance(this.hakusai_tracer.curve.calcTotalDistance() * this.hakusai_fcurve.getValue()); this.hakusai_set.setPosition(this.hakusai_tracer.cv.position); this.tarai_fcurve.execute(Time.deltaTime); this.tarai_tracer.proceedToDistance(this.tarai_tracer.curve.calcTotalDistance() * (1.0f - this.tarai_fcurve.getValue())); SimpleSpline.ControlVertex cv = this.tarai_tracer.getCurrent(); this.tarai_fune.setPosition(cv.position); this.player.controll.cmdSetPosition(cv.position); this.player.controll.cmdSmoothHeadingTo(cv.position - cv.tangent.Y(0.0f)); if (this.tarai_fcurve.isTriggerDone()) { this.ripple_effect.is_created = false; } } break; // 대야에서 내립니다(기슭을 향해 점프). case STEP.GET_OFF_TARAI_0: { this.player_jump.execute(Time.deltaTime); this.player.controll.cmdSetPosition(this.player_jump.position); this.player.controll.cmdSmoothHeadingTo(this.player_jump.goal); } break; // 대야에서 내립니다(조금 걷는다). case STEP.GET_OFF_TARAI_1: { this.player_move.execute(Time.deltaTime); this.player.controll.cmdSetPosition(this.player_move.position.current); this.player.controll.cmdSmoothHeadingTo(this.player_move.position.goal); this.player.playWalkMotion(); } break; // 배추가 돌아옵니다 & 대야가 밖으로 이동. case STEP.CLOSE_DOOR: { this.hakusai_fcurve.execute(Time.deltaTime); this.hakusai_tracer.proceedToDistance(this.hakusai_tracer.curve.calcTotalDistance() * (1.0f - this.hakusai_fcurve.getValue())); this.hakusai_set.setPosition(this.hakusai_tracer.getCurrent().position); this.tarai_fcurve.execute(Time.deltaTime); this.tarai_tracer.proceedToDistance(this.tarai_tracer.curve.calcTotalDistance() * (1.0f - this.tarai_fcurve.getValue())); this.tarai_fune.transform.position = this.tarai_tracer.getCurrent().position; this.player.stopWalkMotion(); } break; } // 대야 뒤에 나오는 물결. if (!this.ripple_effect.is_created || Vector3.Distance(this.ripple_effect.last_position, this.tarai_fune.getPosition()) > 2.0f) { this.ripple_effect.is_created = true; this.ripple_effect.last_position = this.tarai_fune.transform.position; EffectRoot.get().createRipple(this.ripple_effect.last_position); } // ---------------------------------------------------------------- // }
// 現在位置から進む(現在位置からの道のり). public void proceedByDistance(float dist) { do { if (this.is_ended) { break; } ControlVertex cv0; if (this.t < 0.0f) { this.t = 0.0f; } cv0 = this.curve.calcVertex(this.t); this.cv = cv0; // float dt = dist / this.curve.calcTotalDistance() * this.curve.getEnd(); float sdt = dt >= 0.0f ? 1.0f : -1.0f; dt = Mathf.Abs(dt); float dt0 = dt; float t0 = this.t; float advanced = 0.0f; float advanced0 = advanced; int i; // 差大繰り返し回数. int max_times = Mathf.CeilToInt(this.curve.getEnd() / dt); max_times = max_times >= 1 ? max_times : 1; for (i = 0; i < max_times; i++) { t0 = this.t; this.t += sdt * dt; if (this.t >= this.curve.getEnd() && sdt >= 0.0f) { this.t = this.curve.getEnd(); if (dt < dt0) { this.is_ended = true; } dt = Mathf.Max(0.0f, this.t - t0); } else if (this.t < 0.0f && sdt < 0.0f) { this.t = 0.0f; if (dt < dt0) { this.is_ended = true; } dt = Mathf.Max(0.0f, Mathf.Abs(this.t - t0)); } this.cv = this.curve.calcVertex(this.t); advanced0 = advanced; advanced += Vector3.Distance(cv0.position, this.cv.position); if (this.is_ended) { break; } if (advanced >= Mathf.Abs(dist)) { // 行き過ぎた場合. // 進む距離を半分にして、直前の場所からやり直し. dt *= 0.5f; if (dt < dt0 / 100.0f) { break; } this.t = t0; advanced = advanced0; } else { cv0 = this.cv; } } } while(false); }
public override void execute() { CameraControl camera = CameraControl.get(); // ---------------------------------------------------------------- // // 다음 상태로 이행할지 체크합니다.. switch (this.step.do_transition()) { // 이벤트 시작. case STEP.START: { this.step.set_next(STEP.OPEN_DOOR); //camera.module.parallelMoveTo(this.get_locator_position("cam_loc_0")); // 맵에 연관되지 않은 아이템은 가지고 돌아감. bool isPickingup = false; string current_map = MapCreator.get().getCurrentMapName(); ItemController itemYuzu = ItemManager.get().findItem("Yuzu"); if (itemYuzu != null && itemYuzu.isActive()) { if (current_map != itemYuzu.getProduction()) { // 가지고 돌아기는 아이템이 있었다. this.player.controll.cmdItemQueryPick(itemYuzu.name, true, true); isPickingup = true; } } ItemController itemNegi = ItemManager.get().findItem("Negi"); if (itemNegi != null && itemNegi.isActive()) { if (current_map != itemNegi.getProduction()) { // 가지고 돌아가는 아이템이 있었다. this.player.controll.cmdItemQueryPick(itemNegi.name, true, true); isPickingup = true; } } if (this.player.controll.item_carrier.isCarrying() && isPickingup == false) { ItemController item = this.player.controll.item_carrier.getItem(); if (item.isExportable() == false) { // 가지고 나갈 수 없는 것은 두고 갑니다. QueryItemDrop query_drop = this.player.controll.cmdItemQueryDrop(); query_drop.is_drop_done = true; this.player.controll.cmdItemDrop(this.player.controll.account_name); } } } break; // 배추가 옆으로 움직이고 & 대야가 등장. case STEP.OPEN_DOOR: { if (this.hakusai_fcurve.isDone() && this.tarai_fcurve.isDone()) { this.step.set_next(STEP.RIDE_TARAI_0); } } break; // 대야를 탑니다(기슭까지 걷는다). case STEP.RIDE_TARAI_0: { if (!this.player_move.isMoving()) { this.step.set_next(STEP.RIDE_TARAI_1); } } break; // 대야를 탑니다(대야를 향해 점프). case STEP.RIDE_TARAI_1: { if (!this.player_jump.isMoving()) { this.step.set_next(STEP.CLOSE_DOOR); } } break; // 배추가 돌아오고 & 대야가 밖을 향해 이동. case STEP.CLOSE_DOOR: { if (this.hakusai_fcurve.isDone() && this.tarai_fcurve.isDone()) { this.step.set_next(STEP.END); } } break; case STEP.END: { camera.module.popPosture(); this.step.set_next(STEP.IDLE); } break; } // ---------------------------------------------------------------- // // 상태가 전환할 때의 초기화. while (this.step.get_next() != STEP.NONE) { dbwin.console().print(this.step.ToString()); switch (this.step.do_initialize()) { // 이벤트 시작. case STEP.START: { camera.module.pushPosture(); this.player.beginOuterControll(); this.tarai_fune.transform.position = this.tarai_enter_spline.curve.cvs.front().position; } break; // 배추가 옆으로 움직이고 & 대야가 등장. case STEP.OPEN_DOOR: { this.hakusai_set.setControl(true); this.hakusai_fcurve.setSlopeAngle(10.0f, 10.0f); this.hakusai_fcurve.setDuration(4.0f); this.hakusai_fcurve.start(); this.hakusai_tracer.restart(); this.tarai_fcurve.setSlopeAngle(60.0f, 10.0f); this.tarai_fcurve.setDuration(2.0f); this.tarai_fcurve.setDelay(2.0f); this.tarai_fcurve.start(); } break; // 대야를 탑니다(기슭까지 걷는다). case STEP.RIDE_TARAI_0: { this.player_move.position.start = this.player.controll.getPosition(); this.player_move.position.goal = this.get_locator_position("chr_loc_0"); this.player_move.startConstantVelocity(chrBehaviorLocal.MOVE_SPEED); } break; // 대야를 탑니다(대야를 향해서 점프). case STEP.RIDE_TARAI_1: { Vector3 start = this.player.controll.getPosition(); Vector3 goal = this.tarai_enter_spline.curve.cvs.back().position; this.player_jump.start(start, goal, 1.0f); } break; // 배추가 돌아오고 & 대야가 밖을 향해 이동. case STEP.CLOSE_DOOR: { this.hakusai_fcurve.setSlopeAngle(10.0f, 10.0f); this.hakusai_fcurve.setDuration(4.0f); this.hakusai_fcurve.start(); this.hakusai_tracer.restart(); this.hakusai_tracer.setCurrentByDistance(this.hakusai_tracer.curve.calcTotalDistance()); this.tarai_tracer.attach(this.tarai_leave_spline.curve); this.tarai_tracer.restart(); this.tarai_fcurve.reset(); this.tarai_fcurve.setSlopeAngle(20.0f, 60.0f); this.tarai_fcurve.setDuration(2.0f); this.tarai_fcurve.start(); } break; case STEP.END: { // 이벤트 종료. this.hakusai_set.reset(); this.hakusai_set.setControl(false); if (this.is_local_player) { if (this.is_map_change) { GlobalParam.get().skip_enter_event = false; GlobalParam.get().fadein_start = false; if (GlobalParam.get().is_in_my_home) { GameRoot.get().step.set_next(GameRoot.STEP.VISIT); } else { GameRoot.get().step.set_next(GameRoot.STEP.GO_HOME); } // 정원 이동을 알림. if (GlobalParam.get().request_move_home) { Debug.Log("NotifyFieldMoving Leave END."); GameRoot.get().NotifyFieldMoving(); } } else { GlobalParam.get().is_in_my_home = !GlobalParam.get().is_in_my_home; this.player.controll.cmdSetPosition(this.initial_player_position); this.player.endOuterControll(); } } else { // 아이템을 비활성화하고 나서 드롭하지 않으면 재생합니다. ItemBehaviorFruit fruit = this.player.controll.getCarriedItem <ItemBehaviorFruit>(); if (fruit != null) { fruit.activeItem(false); } // 맵 이동이 없을 때 아이템을 가지고 돌아온 경우 아이템의 게임 오브젝트를. // 폐기하지 않게 하고자 아이템을 버립니다. this.player.controll.cmdItemDrop(this.player.name); CharacterRoot.get().deletaCharacter(this.player.controll); } this.player = null; } break; } } // ---------------------------------------------------------------- // // 각 상태에서의 실행 처리. switch (this.step.do_execution(Time.deltaTime)) { // 배추가 옆으로 움직이고 & 대야가 등장. case STEP.OPEN_DOOR: { this.hakusai_fcurve.execute(Time.deltaTime); this.hakusai_tracer.proceedToDistance(this.hakusai_tracer.curve.calcTotalDistance() * this.hakusai_fcurve.getValue()); this.hakusai_set.setPosition(this.hakusai_tracer.getCurrent().position); this.tarai_fcurve.execute(Time.deltaTime); this.tarai_tracer.proceedToDistance(this.tarai_tracer.curve.calcTotalDistance() * this.tarai_fcurve.getValue()); this.tarai_fune.setPosition(this.tarai_tracer.getCurrent().position); if (this.tarai_fcurve.isTriggerDone()) { this.ripple_effect.is_created = false; } } break; // 대야를 탑니다(기슭까지 걷는다). case STEP.RIDE_TARAI_0: { this.player_move.execute(Time.deltaTime); this.player.controll.cmdSetPosition(this.player_move.position.current); this.player.controll.cmdSmoothHeadingTo(this.player_move.position.goal); this.player.playWalkMotion(); } break; // 대야를 탑니다(대야를 향해서 점프). case STEP.RIDE_TARAI_1: { this.player_jump.execute(Time.deltaTime); this.player.controll.cmdSetPosition(this.player_jump.position); this.player.controll.cmdSmoothHeadingTo(this.player_jump.goal); this.player.stopWalkMotion(); if (this.player_jump.is_trigger_bounce && this.player_jump.bounce_count == 1) { this.ripple_effect.is_created = false; } } break; // 배추가 돌아오고 & 대야가 밖을 향해 이동. case STEP.CLOSE_DOOR: { this.hakusai_fcurve.execute(Time.deltaTime); this.hakusai_tracer.proceedToDistance(this.hakusai_tracer.curve.calcTotalDistance() * (1.0f - this.hakusai_fcurve.getValue())); this.hakusai_set.setPosition(this.hakusai_tracer.cv.position); this.tarai_fcurve.execute(Time.deltaTime); this.tarai_tracer.proceedToDistance(this.tarai_tracer.curve.calcTotalDistance() * this.tarai_fcurve.getValue()); SimpleSpline.ControlVertex cv = this.tarai_tracer.getCurrent(); this.tarai_fune.setPosition(cv.position); this.player.controll.cmdSetPosition(cv.position); this.player.controll.cmdSmoothHeadingTo(cv.position + cv.tangent.Y(0.0f)); this.player.stopWalkMotion(); } break; } // 대야 뒤에 나오는 물결. if (!this.ripple_effect.is_created || Vector3.Distance(this.ripple_effect.last_position, this.tarai_fune.getPosition()) > 2.0f) { this.ripple_effect.is_created = true; this.ripple_effect.last_position = this.tarai_fune.transform.position; EffectRoot.get().createRipple(this.ripple_effect.last_position); } // ---------------------------------------------------------------- // }