研究了2天,发现有mirro这个插件,但是担心不会用 或者不灵活,我自己用udp写了一个同步的demo
其它的解决方案
unet的networkmanager组件用于局域网选的一个客户端当做服务端不能外网 。2019已经移除
还有 Photon
后台我用java写的
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BootNettyUdpServer {
Logger logger = LoggerFactory.getLogger(BootNettyUdpServer.class);
/**
* 启动服务
*/
public void bind(int port) {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
//UDP方式使用Bootstrap
Bootstrap serverBootstrap = new Bootstrap();
serverBootstrap = serverBootstrap.group(eventLoopGroup);
serverBootstrap = serverBootstrap.channel(NioDatagramChannel.class);
serverBootstrap = serverBootstrap.option(ChannelOption.SO_BROADCAST, true);
//不需要太多其他的东西,直接这样就可以用
serverBootstrap = serverBootstrap.handler(new BootNettyUdpSimpleChannelInboundHandler());
System.out.println("netty udp start!");
ChannelFuture f = serverBootstrap.bind(port).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
logger.error("bind", e);
} finally {
System.out.println("netty udp close!");
eventLoopGroup.shutdownGracefully();
}
}
}
public class BootNettyUdpSimpleChannelInboundHandler extends SimpleChannelInboundHandler<DatagramPacket> {
Logger logger = LoggerFactory.getLogger(BootNettyUdpSimpleChannelInboundHandler.class);
static Set<InetSocketAddress> sets = new HashSet<>();
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
try {
String strdata = msg.content().toString(CharsetUtil.UTF_8);
//打印收到的消息
System.out.println("msg---" + strdata);
for (InetSocketAddress set : sets) {
if(set.equals(msg.sender())){
continue;
}
ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(strdata, CharsetUtil.UTF_8), set));
}
sets.add(msg.sender());
System.out.println(sets.size());
} catch (Exception e) {
logger.error("channelRead0", e);
}
}
}
@EnableAsync
@EnableCaching
@RestController
@SpringBootApplication
@MapperScan("com.hewei.game.mapper")
public class UploadApplication implements CommandLineRunner {
@RequestMapping("/test")
public String say(){
return "Hello Spring Boot!";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(UploadApplication.class, args);
}
@Async
@Override
public void run(String... args) throws Exception {
/**
* 使用异步注解方式启动netty udp服务端服务
*/
new BootNettyUdpServer().bind(9999);
}
}
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>file-upload</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>file-upload</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
unity端
public class Cube : MonoBehaviour {
public static Transform transform0;
public float speed = 10f;
// Start is called before the first frame update
void Start() {
transform0 = transform;
string json = JsonUtility.ToJson(transform.position);
print(json);
InvokeRepeating("aa", 1f, 0.1f);
}
// Update is called once per frame
void Update() {
float horizon = Input.GetAxis("Horizontal"); //获得horizontal
transform.position += Vector3.right * speed * horizon * Time.deltaTime;
}
void aa() {
// String s = "{\"x\":0.0,\"y\":0.0,\"z\":0.0}";
// transform.position = JsonUtility.FromJson<Vector3>(s);
var json = JsonUtility.ToJson(transform.position);
TcpTool.instance.SocketSend("c:" + json);
}
}
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
public class TcpTool {
public static readonly TcpTool instance = dd();
static TcpTool dd() {
TcpTool instance0 = new TcpTool();
instance0.InitSocket();
return instance0;
}
//以下默认都是私有的成员
Socket socket; //目标socket
EndPoint clientEnd; //客户端
IPEndPoint ipEnd; //侦听端口
string recvStr; //接收的字符串
string sendStr; //发送的字符串
byte[] recvData = new byte[1024]; //接收的数据,必须为字节
byte[] sendData = new byte[1024]; //发送的数据,必须为字节
int recvLen; //接收的数据长度
Thread connectThread; //连接线程
//初始化
public void InitSocket() {
//定义侦听端口,侦听任何IP
ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 7778);
//定义套接字类型,在主线程中定义
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//服务端需要绑定ip
socket.Bind(ipEnd);
//定义客户端
IPEndPoint sender = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
clientEnd = (EndPoint)sender;
Debug.Log("waiting for UDP dgram");
//开启一个线程连接,必须的,否则主线程卡死
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
public void SocketSend (string sendStr) {
//数据类型转换
sendData = Encoding.ASCII.GetBytes(sendStr);
//发送给指定客户端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEnd);
}
//服务器接收
public void SocketReceive() {
//进入接收循环
while( true ) {
//对data清零
recvData = new byte[1024];
//获取客户端,获取客户端数据,用引用给客户端赋值
recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
Debug.Log("message from: " + clientEnd.ToString()); //打印客户端信息
//输出接收到的数据
recvStr = Encoding.ASCII.GetString(recvData, 0, recvLen);
string substring = recvStr.Substring(2);
Debug.Log("接收到数据截取后" + substring);
ThreadHelper.QueueOnMainThread(() => {
Cube.transform0.position = JsonUtility.FromJson<Vector3>(substring);
});
}
}
//连接关闭
public void SocketQuit() {
//关闭线程
if (connectThread != null) {
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
Debug.Log("disconnect");
}
}
using UnityEngine;
using System.Collections.Generic;
using System;
using System.Threading;
public class ThreadHelper : MonoBehaviour {
private static ThreadHelper Current;
private List<Action> actions = new List<Action>();
private static Thread mainThread;
public static bool IsMainThread() {
return Thread.CurrentThread == mainThread;
}
// Use this for initialization
void Awake() {
Current = this;
mainThread = Thread.CurrentThread;
}
// Update is called once per frame
void Update() {
var currentActions = new List<Action>();
lock (actions) {
currentActions.AddRange(actions);
foreach (var item in currentActions)
actions.Remove(item);
}
foreach (var action in currentActions) {
action();
}
}
public static void QueueOnMainThread (Action action) {
if (IsMainThread()) {
action();
return;
}
lock (Current.actions) {
Current.actions.Add(action);
}
}
public static void QueueOnThreadPool (WaitCallback callBack, object state = null) {
ThreadPool.QueueUserWorkItem(callBack, state);
}
}
using System;
using UnityEngine;
public class Cube : MonoBehaviour {
public static Transform transform0;
public float speed = 10f;
// Start is called before the first frame update
void Start() {
transform0 = transform;
string json = JsonUtility.ToJson(transform.position);
print(json);
InvokeRepeating("aa", 1f, 0.1f);
}
// Update is called once per frame
void Update() {
float horizon = Input.GetAxis("Horizontal"); //获得horizontal
transform.position += Vector3.right * speed * horizon * Time.deltaTime;
}
void aa() {
// String s = "{\"x\":0.0,\"y\":0.0,\"z\":0.0}";
// transform.position = JsonUtility.FromJson<Vector3>(s);
var json = JsonUtility.ToJson(transform.position);
TcpTool.instance.SocketSend("c:" + json);
}
}