flocking코드를 분석하면서 GetNeighbors함수 부분에서 class를 타입으로 받아오는 함수가 있었다. 
처음에 이해가 안돼서 분석을 하였다. 아래는 선생님이 알려주신 부분이다. 

우선 Enemy라는 클래스가 하나 따로 존재하고 있다고 생각하자. 
클래스의 개념은 함수와 변수의 집합으로 생각하면 편하다. 
일반적으로 기본적인 스크립트의 구조는 하나의 클래스로 존재한다. 
public class Enemy
{
    public float hp = 100.0f;
    public float speed = 10.0f;
    public Vector3 direction;
    public UnityEngine.GameObject prefab;
}

EnemyInit라는 우리의 메인 클래스를 보자. 
EnemyInit에서 우선 변수 타입이 List<Enemy>인 enemies라는 변수를 만든다. 
   public List<Enemy> enemies = new List<Enemy>();
*여기에서 List<Enemy>의 뜻은 Enemy클래스를 list로 받아온다는 의미이다. 
즉, 이 말은 Enemy클래스가 마치 엑셀처럼 줄이 리스트 형태로 가져와 진다는 의미이다. 

start함수에서 enemies리스트에 enemy를 생성하여서 추가한다. 
 void Start()
    {
        //Add Eenemy ...
        for(int i=0; i<5;i++)
        {
            Enemy em = new Enemy();
            em.hp = 100.0f * i;
            em.speed = 10.0f * i;
            enemies.Add(em);
        }

그 다음,
타입이 List<Enemy>인 GetEnemyList()라는 반환함수를 만든다. 
     List<Enemy> GetEnemyList()
*우리가 일반적으로 만드는 void붙는 함수는 호출함수라고 한다.
반환함수는 특정 값을 반환하는 함수를 의미하며 주로 매개변수와 함께 사용한다.
* 매개 변수는 변화하는 변수라고 하여,
함수를 정의할 때 함께 지정해주는 변수값에 들어가는 값을 매개변수라 이해하면 된다. 

반환할 값이 list타입이기 때문에 반환할 리스트인 returnEnemies라는 리스트를 새로만든다. 
foreach 반복문에서 enemies리스트에 안에 있는 변수를 enemy변수로 대입한다. 
List<Enemy> returnEnemies = new List<Enemy>();
 
        foreach (Enemy enemy in enemies)
        {
          
        }

만약 hp가 200.0이상이라면 enemy를 아까 생성해둔 returnEnemies리스트 안에 넣는다. 
 if (enemy.hp >= 200.0f)
            {
                returnEnemies.Add(enemy);
            }

그리고 반환 함수는 항상 return값을 반환해야 한다. 
        return returnEnemies;


리스트 안에 리스트 있고,,, 또 리스트 불러오고.... 으아아아아아악....


<전체코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Enemy
{
    public float hp = 100.0f;
    public float speed = 10.0f;
    public Vector3 direction;
    public UnityEngine.GameObject prefab;
}
 
public class EnemyInit : MonoBehaviour
{
    public List<Enemy> enemies = new List<Enemy>();
 
    void Start()
    {
        //Add Eenemy ...
        for(int i=0; i<5;i++)
        {
            Enemy em = new Enemy();
            em.hp = 100.0f * i;
            em.speed = 10.0f * i;
            enemies.Add(em);
        }
 
        //가까이 있는 적을 추출한다.
        List<Enemy> closestEnemy = GetEnemyList();
    }
 
    List<Enemy> GetEnemyList()
    {
        List<Enemy> returnEnemies = new List<Enemy>();
 
        foreach (Enemy enemy in enemies)
        {
            if (enemy.hp >= 200.0f)
            {
                returnEnemies.Add(enemy);
            }
        }
 
        return returnEnemies;
    }
}
cs


Posted by 도이(doi)
,

이 포스트에 있는 코드는 모두 아래 링크에 있는 튜토리얼을 기반으로 이루어졌습니다. 

https://www.youtube.com/watch?v=payFhNs9hvs&list=PL4CCSwmU04MhfoJTJWA7n2AIB4dq6umeu




- Level

- Member

- Enemy

- MemberConfig


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Level : MonoBehaviour {
 
    public Transform memberPrefab;
    public Transform enemyPrefab;
    public int numberOfMembers;
    public int numberOfEnemies;
    public List<Member> members;
    public List<Enemy> enemies;
    public float bounds; //이동 범위
    public float spawnRadius; //생성 범위
 
    void Start () {
        //리스트 생성
        members = new List<Member>(); 
        enemies = new List<Enemy>();
 
        //생성 함수
        Spawn(memberPrefab, numberOfMembers);
        Spawn(enemyPrefab, numberOfEnemies);
 
        //리스트에 member, enemy 추가   
        members.AddRange(FindObjectsOfType<Member>());
        enemies.AddRange(FindObjectsOfType<Enemy>());
    }
    
    void Spawn(Transform prefab, int count)
    {
        //임의의 위치에 agent와 enemy 생성하기 
        for (int i = 0; i < count; i++)
        {
            Instantiate(prefab, 
                        new Vector3(Random.Range(-spawnRadius, spawnRadius), 
                                    Random.Range(-spawnRadius, spawnRadius), 
                                    0),
                        Quaternion.identity);
        }
    }
 
    //이웃을 분류하는 리스트 
    public List<Member> GetNeighbors(Member member, float radius)
    {
        //이웃을 반환할 리스트 생성 
        List<Member> neighborsFound = new List<Member>();
 
        //members리스트 안에 있는 member중 하나를 othermember라는 변수로 지정 
        foreach(Member otherMember in members)
        {
            if (otherMember == member)
                continue//조건 성립하면 다시 돌아감 
        
            //본인의 위치가 othermember와 일정 거리 가까우면 
            if(Vector3.Distance(member.position, otherMember.position) <= radius)
            {
                //neighborsFound 리스트에 otherMember를 추가
                neighborsFound.Add(otherMember);
            }
        }
        return neighborsFound; //neighborsFound 반환 
    }
 
    //Enemy를 분류하는 리스트 
    public List<Enemy> GetEnemies(Member member, float radius)
    {
        //enemy를 반환할 리스트 생성 
        List<Enemy> returnEnemies = new List<Enemy>();
 
        //enemies리스트 안에 있는 enemy중 하나를 enemy라는 변수로 지정
        foreach (Enemy enemy in enemies)
        {
            //본인의 위치와 enemy의 위치가 일정 거리 가까우면 
            if(Vector3.Distance(member.position, enemy.transform.position) <= radius)
            {
                //returnEnemies 리스트에 enemy를 추가 
                returnEnemies.Add(enemy);
            }
        }
        return returnEnemies; //returnEnemies 반환 
    }
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Member : MonoBehaviour {
 
    public Vector3 position;
    public Vector3 velocity;
    public Vector3 acceleration;
 
    public Level level;
    public MemberConfig conf;
 
    Vector3 wanderTarget;
 
    private void Start()
    {
        position = transform.position;
 
        //FindObjectOfType은 getComponent와 유사하지만 전역적으로 component를 찾아온다 
        level = FindObjectOfType<Level>();
        conf = FindObjectOfType<MemberConfig>();
 
        position = transform.position;
        velocity = new Vector3(Random.Range(-33), Random.Range(-33), 0);
    }
 
    private void Update()
    {
        acceleration = Combine(); //이동함수 호출 
        acceleration = Vector3.ClampMagnitude(acceleration, conf.maxAccelaeration);//ClampMagnitude: max값에 도달하면 그 이후의 값은 max로 반환(상한선)
        velocity = velocity + acceleration * Time.deltaTime; //속도 
        velocity = Vector3.ClampMagnitude(velocity, conf.maxVelocity); 
        position = position + velocity * Time.deltaTime; //위치 
 
        WrapAround(ref position, -level.bounds, level.bounds); //범위 벗어날 경우
        //이동방향으로 회전
        transform.rotation = Quaternion.LookRotation(position - transform.position);
        //위치 할당 
        transform.position = position; 
    }
 
    //이동 함수1 - 모였다 퍼졌다 
    protected Vector3 Wander()
    {
        //이동 크기 
        float jitter = conf.wanderJitter * Time.deltaTime; //jitter(진폭)변수 생성
        wanderTarget += new Vector3(0, RandomBinomial() * jitter, 0); //랜덤이항식*jitter으로 이동 
        wanderTarget = wanderTarget.normalized; //정규화: 방향에 상관없이 똑같은 크기를 받도록 
        wanderTarget *= conf.wanderRadius; //?? 이동 지름을 곱해줌 
        //위치 좌표 
        Vector3 targetInLocalSpace = wanderTarget + new Vector3(conf.wanderDistance, conf.wanderDistance, 0); //타겟이 로컬좌표(상대위치)에 있는, ??wanderDistance변수만큼 y축으로 더해줌 
        Vector3 targetInWorldSpace = transform.TransformPoint(targetInLocalSpace); //월드좌표(절대위치), transformpoint: local좌표 -> world좌표로 변환 
        targetInLocalSpace -= this.position; //방향
        return targetInLocalSpace.normalized; //로컬좌표타겟 변수 정규화
    }
 
    //이동 함수2 - wander함수보다 더 랜덤하게 이동 
    Vector3 Cohesion()
    {
        Vector3 cohesionVector = new Vector3();
        int countMembers = 0;
        var neighbors = level.GetNeighbors(this, conf.cohesionRadius); 
        if (neighbors.Count == 0
            return cohesionVector;
        //??neighbors리스트에 있는 member에 관한 반복문 
        foreach(var member in neighbors)
        {
            //isInFOV: 시야각 공식 
            if (isInFOV(member.position))
            {
                cohesionVector += member.position;
                countMembers++;
            }
        }
        if (countMembers == 0)
            return cohesionVector;
        //??
        cohesionVector /= countMembers; 
        cohesionVector = cohesionVector - this.position;
        cohesionVector = Vector3.Normalize(cohesionVector);
        return cohesionVector;
    }
 
    //alignment(회전) - 이웃과 속도(벡터값 - 크기와 방향 가짐)를 맞추는 함수  
    Vector3 Alignment()
    {
        Vector3 alignVector = new Vector3(); //새로운 변수 공간 생성 
        var members = level.GetNeighbors(this, conf.alignmentRadius); //member로 현재 스크립트가 적용된 gameobject를 가져온다, conf.alignmentRadius는 alignment 적용거리의 범위(collider)이다
 
        if (members.Count == 0)
            return alignVector;
        // 자신의 시야각에 멤버가 존재하면 정렬을 맞춤 
        foreach (var member in members)
        {
            //만약 시야각에 member가 들어오면, alignVector에 시야각에 들어온 member의 벡터값을 더해준다
            if (isInFOV(member.position))
                alignVector += member.velocity; //alignVector에 member의 속도(벡터값)를 더해주는 
        }
        return alignVector.normalized;
    }
 
    //separation - 이웃과 일정한 거리를 띄우는 
    Vector3 Separation()
    {
        Vector3 separteVector = new Vector3();
        var members = level.GetNeighbors(this, conf.separationRadius);
        if (members.Count == 0)
            return separteVector;
 
        foreach(var member in members)
        {
            if(isInFOV(member.position))
            {
                Vector3 movingTowards = this.position - member.position; //member에서 멀어지는 자신의 방향
                if(movingTowards.magnitude > 0)//만약 자신이 member와 접촉되지 않았다면 
                {
                    separteVector += movingTowards.normalized / movingTowards.magnitude; //1의 값(정규화 값)을 기준으로 자신의 원래 스칼라 값을 나누어주어서 이동해야되는 크기에 따라 속도를 정의한다 ,magnitude: 벡터의 길이(크기) 
                }
            }
        }
        return separteVector.normalized;
    }
 
    Vector3 Avoidance()
    {
        Vector3 avoidVector = new Vector3();
        var enemyList = level.GetEnemies(this, conf.avoidanceRadius); //level스크립트에서 enemy리스트 가져오기 
        if (enemyList.Count == 0)
            return avoidVector;
        foreach(var enemy in enemyList)
        {
            avoidVector += RunAway(enemy.transform.position); //적과 반대로 이동하는 
        }
        return avoidVector.normalized;
    }
 
    Vector3 RunAway(Vector3 target)
    {
        Vector3 neededVelocity = (position - target).normalized * conf.maxVelocity; //적(target)의 위치와 반대방향으로 이동
        return neededVelocity - velocity; //적을 피해 이동하는 벡터 - member가 원래 가야되는 
    }
 
    //최종 조합 - wander() + cohesion() + alignment() + separation() + avoidance()의 결합 (snake game처럼 보이는)
    virtual protected Vector3 Combine()
    {
        Vector3 finalVec = conf.cohesionPriority * Cohesion() + conf.wanderPriority * Wander()
                               + conf.alignmentPriority * Alignment() + conf.separationPriority * Separation()
                               + conf.avoidancePriority * Avoidance();
        return finalVec;
    }
 
    //agent가 범위를 벗어나갈 때 초기값으로 이동시키는 함수  
    void WrapAround(ref Vector3 vector, float min, float max)
    {
        //wrapAroundfloat이라는 공식을 반환함 
        vector.x = WrapAroundFloat(vector.x, min, max);
        vector.y = WrapAroundFloat(vector.y, min, max);
        vector.z = 0;//WrapAroundFloat(vector.z, min, max); //2D, 3D
    }
 
    //wrapAroundFloat이라는 공식 
    float WrapAroundFloat(float value, float min, float max)
    {
        if (value > max)
            value = min;
        else if (value < min)
            value = max;
        return value;
    }
 
    //랜덤 매개변수 이항식 뺄셈 
    float RandomBinomial()
    {
        //random은 c언어에서는 0부터 1값만 나온다 
        //return Random.Range(0f, 1f) - Random.Range(0f, 1f);
        return Random.Range(-1.0f, 1.0f);
    }
 
    //fov: 시야각 - '속도'와 '(변수)-현재 위치' 사이의 각도의 반환값이 conf.maxFOV보다 작을 때 
    bool isInFOV(Vector3 vec)
    {
        return Vector3.Angle(this.velocity, vec - this.position) <= conf.maxFOV;
    }
 
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Enemy : MonoBehaviour { //member스크립트 상속
 
    private RaycastHit hit;
    private Vector3 movePos;
 
    private void Start()
    {
        movePos = transform.position;
    }
 
    private void Update()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit, 10000))
        {
            movePos = hit.point;
        }
 
        //sqrMagnitude는 벡터의 길이의 제곱한 값을 반환.루트 연산을 하지 않기 때문에 Magnitude보다 속도가 훨씬 빠르다 
        if ((movePos - transform.position).sqrMagnitude >= 0.2f * 0.2f)
        {
            Quaternion rot = Quaternion.LookRotation(hit.point - transform.position);
            transform.rotation = Quaternion.Slerp(transform.rotation, rot, Time.deltaTime * 10.0f);
            transform.Translate(Vector3.forward * Time.deltaTime * 10.0f);
        }
 
    }
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class MemberConfig : MonoBehaviour {
 
    public float maxFOV = 180//시야각
    public float maxAccelaeration; //가속도
    public float maxVelocity; //속도(방향 + 속력)
 
    //Wander Varaibles
    public float wanderJitter; //진폭
    public float wanderRadius; //적용 범위
    public float wanderDistance; //거리
    public float wanderPriority; //가중치(weight)
 
    //Cohesion Varaibles
    public float cohesionRadius; 
    public float cohesionPriority;
 
    //Alignment Varaibles
    public float alignmentRadius; 
    public float alignmentPriority; 
 
    //Separation Varaibles
    public float separationRadius; 
    public float separationPriority; 
 
    //Avoidance Varaibles
    public float avoidanceRadius;
    public float avoidancePriority; 
 
}
 
cs


Posted by 도이(doi)
,



photonView.IsMine

True if the PhotonView is "mine" and can be controlled by this client.

PUN has an ownership concept that defines who can control and destroy each PhotonView. True in case the owner matches the local Player. True if this is a scene photonview on the Master client.


OnPhotonSerializeView

Called by PUN several times per second, so that your script can write and read synchronization data for the PhotonView.

This method will be called in scripts that are assigned as Observed component of a PhotonView.
PhotonNetwork.SerializationRate affects how often this method is called.
PhotonNetwork.SendRate affects how often packages are sent by this client.

Implementing this method, you can customize which data a PhotonView regularly synchronizes. Your code defines what is being sent (content) and how your data is used by receiving clients.

Unlike other callbacks, OnPhotonSerializeView only gets called when it is assigned to a PhotonView as PhotonView.observed script.

To make use of this method, the PhotonStream is essential. It will be in "writing" mode" on the client that controls a PhotonView (PhotonStream.IsWriting == true) and in "reading mode" on the remote clients that just receive that the controlling client sends.

If you skip writing any value into the stream, PUN will skip the update. Used carefully, this can conserve bandwidth and messages (which have a limit per room/second).

Note that OnPhotonSerializeView is not called on remote clients when the sender does not send any update. This can't be used as "x-times per second Update()".

Implemented in PhotonAnimatorViewCullingHandlerPhotonRigidbody2DViewPhotonRigidbodyViewPhotonTransformView, and SmoothSyncMovement.


놀러가야 돼서 해석은 차후에...



분석한 스크립트(위 도식과 함께 볼 것!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using Valve.VR;
 
public class cntrlNet : MonoBehaviourPunCallbacks, IPunObservable {
 
    private Transform tr;
 
    private GameObject tiltBrush;
 
    public SteamVR_Input_Sources any = SteamVR_Input_Sources.Any;
    public SteamVR_Action_Boolean triggerAction;
 
    private bool isTriggerDown = false;
 
    void Start()
    {
        tr = GetComponent<Transform>();
 
        tiltBrush = GameObject.Find("tiltBrush");
        tiltBrush.SetActive(false);
 
        if (SteamVR_Input.initialized == false)
        {
            Debug.Log("cntrl is not detected");
            return;
        }
    }
 
    void Update () {
        //controlled locally일 경우 이동(자기 자신의 캐릭터일 때)
        if (photonView.IsMine)
        {
//트리거 상태를 체크해서 보내기 위한(currTrigger에 보내는)
            if (triggerAction.GetState(SteamVR_Input_Sources.Any))
            {
                isTriggerDown = true;
            }
            else
            {
                isTriggerDown = false;
            }
 
            if (SteamVR_Input._default.inActions.InteractUI.GetStateDown(SteamVR_Input_Sources.Any))
            {
                tiltBrush.SetActive(true);
            }
            else if (SteamVR_Input._default.inActions.InteractUI.GetStateUp(SteamVR_Input_Sources.Any))
            {
                tiltBrush.SetActive(false);
            }
        } 
        else
        {
            //끊어진 시간이 너무 길 경우(텔레포트)
            if ((tr.position - currPos).sqrMagnitude >= 10.0f * 10.0f)
            {
                tr.position = currPos;
                tr.rotation = currRot;
            }
            //끊어진 시간이 짧을 경우(자연스럽게 연결 - 데드레커닝)
            else
            {
                tr.position = Vector3.Lerp(tr.position, currPos, Time.deltaTime * 10.0f);
                tr.rotation = Quaternion.Slerp(tr.rotation, currRot, Time.deltaTime * 10.0f);
            }
 
            tiltBrush.SetActive(currTrigger);
        }
 
    }
 
    //클론이 통신을 받는 변수 설정
    private Vector3 currPos;
    private Quaternion currRot;
    private bool currTrigger;
 
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        //통신을 보내는 
        if (stream.IsWriting)
        {
            stream.SendNext(tr.position);
            stream.SendNext(tr.rotation);
            stream.SendNext(isTriggerDown);
        }
 
        //클론이 통신을 받는 
        else
        {
            currPos = (Vector3)stream.ReceiveNext();
            currRot = (Quaternion)stream.ReceiveNext();
            currTrigger = (bool)stream.ReceiveNext();
        }
    }
}
  cs




[참조]

http://itmining.tistory.com/25 

http://doc-api.photonengine.com/en/pun/v2/index.html

Posted by 도이(doi)
,

htc Vive와 kinect를 네트워크를 통해서 연결시켜보자. 

- microsoft사에서 제공하는 kinect 유니티 패키지 사용

- steamVR plugin 사용

- photonNetwork 사용

우선, 2대의 컴퓨터에 각각 kinect와 htcVive를 설치한다. (설치 방법은 생략) 



구조

1. SceneSetting

2. htcVive용 Kinect용 분리시키기(다른 타입의 player 생성)

3. htcVive pc Setting & Kinect pc Setting

4. tiltBrush 동기화 시키기 




1. SceneSetting






2. htcVive용 Kinect용 분리시키기

mode선택 enum을 사용하는 스크립트 작성하기. 

kinect용 pc와 htcVive용 pc에 각각 적합한 모드를 선택해줄 것. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public enum PlayerType
{
    none = -1
    HTCvive = 0
    Kinect = 1
}
 
public class Mode : MonoBehaviour {
 
    public PlayerType playerType = PlayerType.none;
 
    public static Mode instance = null;
 
    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else if (instance != this)
        {
            Destroy(this.gameObject);
        }
        DontDestroyOnLoad(this);
    }
}
 
cs


mode스크립트는 모드에 따라서 네트워크 연결시 각각 다른 player를 instantiate한다. 

위의 환경 세팅에 만들어 둔 Resources폴더에 

미리, VivePlayer와 KinectPlayer를 만들어 두었던 것을 불러온다.  




3. htcVive & Kinect 네트워크 작업 구조도 




4. tiltBrush 동기화 시키기 

htcVive 컨트롤러 tiltBrush 동기화 시키기 (CtrlNet스크립트)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using Valve.VR;
 
public class cntrlNet : MonoBehaviourPunCallbacks, IPunObservable {
 
    private Transform tr;
 
    private GameObject tiltBrush;
 
    public SteamVR_Input_Sources any = SteamVR_Input_Sources.Any;
    public SteamVR_Action_Boolean triggerAction;
 
    private bool isTriggerDown = false;
 
    void Awake()
    {
        SteamVR_Behaviour_Pose steamctrl=gameObject.GetComponent<SteamVR_Behaviour_Pose>();
 
        if (photonView.IsMine == false)
        {
            steamctrl.enabled = false;
        }
    }
 
    void Start()
    {
        tr = GetComponent<Transform>();
 
        tiltBrush = GameObject.Find("tiltBrush");
        tiltBrush.SetActive(false);
 
        if (SteamVR_Input.initialized == false)
        {
            Debug.Log("cntrl is not detected");
            return;
        }
    }
 
    void Update () {
        //controlled locally일 경우 이동(자기 자신의 캐릭터일 때)
        if (photonView.IsMine)
        {
            if (triggerAction.GetState(SteamVR_Input_Sources.Any))
            {
                isTriggerDown = true;
            }
            else
            {
                isTriggerDown = false;
            }
 
            if (SteamVR_Input._default.inActions.InteractUI.GetStateDown(SteamVR_Input_Sources.Any))
            {
                tiltBrush.SetActive(true);
            }
            else if (SteamVR_Input._default.inActions.InteractUI.GetStateUp(SteamVR_Input_Sources.Any))
            {
                tiltBrush.SetActive(false);
            }
        } 
        else
        {
            //끊어진 시간이 너무 길 경우(텔레포트)
            if ((tr.position - currPos).sqrMagnitude >= 10.0f * 10.0f)
            {
                tr.position = currPos;
                tr.rotation = currRot;
            }
            //끊어진 시간이 짧을 경우(자연스럽게 연결 - 데드레커닝)
            else
            {
                tr.position = Vector3.Lerp(tr.position, currPos, Time.deltaTime * 10.0f);
                tr.rotation = Quaternion.Slerp(tr.rotation, currRot, Time.deltaTime * 10.0f);
            }
 
            tiltBrush.SetActive(currTrigger);
        }
 
    }
 
    //클론이 통신을 받는 변수 설정
    private Vector3 currPos;
    private Quaternion currRot;
    private bool currTrigger;
 
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        //통신을 보내는 
        if (stream.IsWriting)
        {
            stream.SendNext(tr.position);
            stream.SendNext(tr.rotation);
            stream.SendNext(isTriggerDown);
        }
 
        //클론이 통신을 받는 
        else
        {
            currPos = (Vector3)stream.ReceiveNext();
            currRot = (Quaternion)stream.ReceiveNext();
            currTrigger = (bool)stream.ReceiveNext();
        }
    }
}
 
cs


 

Posted by 도이(doi)
,