gpt4 book ai didi

java - 如何使用Cloud Anchor和Sceneform构建ARCore多人游戏?

转载 作者:搜寻专家 更新时间:2023-11-01 03:31:21 28 4
gpt4 key购买 nike

目前我正在探索阿科尔和云锚。我能够使用sceneform和云锚创建一个应用程序。基本上,我们可以使用device-1在任何曲面上放置一个对象,并且device-2可以使用云锚共享相同的体验。
解析云锚后,无法侦听对象转换中的任何更改,即如果用户在device-1上旋转或移动对象,则在device-2上没有任何有关该对象的信息。
如果我们想使用云锚创建多人体验,我找到的唯一方法是将对象的状态与服务器和两个设备同步。
我的问题是:
这是在arcore中实现多人同步的正确方法吗?
如果此方法可行,我们需要同步哪些属性?

最佳答案

要使用Cloud Anchors技术构建多人应用程序,请使用following project作为起点:

package com.google.ar.core.codelab.cloudanchor;

public class MainActivity extends AppCompatActivity implements GLSurfaceView.Renderer {

private static final String TAG = MainActivity.class.getSimpleName();
private GLSurfaceView surfaceView;
private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
private final ObjectRenderer virtualObject = new ObjectRenderer();
private final ObjectRenderer virtualObjectShadow = new ObjectRenderer();
private final PlaneRenderer planeRenderer = new PlaneRenderer();
private final PointCloudRenderer pointCloudRenderer = new PointCloudRenderer();
private final float[] anchorMatrix = new float[16];
private final float[] projectionMatrix = new float[16];
private final float[] viewMatrix = new float[16];
private final float[] colorCorrectionRgba = new float[4];
private final Object singleTapAnchorLock = new Object();

@GuardedBy("singleTapAnchorLock")
private MotionEvent queuedSingleTap;
private final SnackbarHelper snackbarHelper = new SnackbarHelper();
private GestureDetector gestureDetector;
private DisplayRotationHelper displayRotationHelper;
private Session session;
private boolean installRequested;

@Nullable
@GuardedBy("singleTapAnchorLock")
private Anchor anchor;

private void handleTapOnDraw(TrackingState currentTrackingState, Frame currentFrame) {
synchronized (singleTapAnchorLock) {
if (anchor == null
&& queuedSingleTap != null
&& currentTrackingState == TrackingState.TRACKING) {
for (HitResult hit : currentFrame.hitTest(queuedSingleTap)) {
if (shouldCreateAnchorWithHit(hit)) {
Anchor newAnchor = hit.createAnchor();
setNewAnchor(newAnchor);
break;
}
}
}
queuedSingleTap = null;
}
}

private static boolean shouldCreateAnchorWithHit(HitResult hit) {
Trackable trackable = hit.getTrackable();
if (trackable instanceof Plane) {
return ((Plane) trackable).isPoseInPolygon(hit.getHitPose());
} else if (trackable instanceof Point) {
return ((Point) trackable).getOrientationMode() == OrientationMode.ESTIMATED_SURFACE_NORMAL;
}
return false;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = findViewById(R.id.surfaceview);
displayRotationHelper = new DisplayRotationHelper(/*context=*/ this);

gestureDetector =
new GestureDetector(
this,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
synchronized (singleTapAnchorLock) {
queuedSingleTap = e;
}
return true;
}

@Override
public boolean onDown(MotionEvent e) {
return true;
}
});
surfaceView.setOnTouchListener((unusedView, event) -> gestureDetector.onTouchEvent(event));
surfaceView.setPreserveEGLContextOnPause(true);
surfaceView.setEGLContextClientVersion(2);
surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
surfaceView.setRenderer(this);
surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
installRequested = false;

Button clearButton = findViewById(R.id.clear_button);
clearButton.setOnClickListener(
(unusedView) -> {
synchronized (singleTapAnchorLock) {
setNewAnchor(null);
}
});
}

@Override
protected void onResume() {
super.onResume();

if (session == null) {
Exception exception = null;
int messageId = -1;
try {
switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) {
case INSTALL_REQUESTED:
installRequested = true;
return;
case INSTALLED:
break;
}

if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this);
return;
}
session = new Session(this);
} catch (UnavailableArcoreNotInstalledException e) {
messageId = R.string.snackbar_arcore_unavailable;
exception = e;
} catch (UnavailableApkTooOldException e) {
messageId = R.string.snackbar_arcore_too_old;
exception = e;
} catch (UnavailableSdkTooOldException e) {
messageId = R.string.snackbar_arcore_sdk_too_old;
exception = e;
} catch (Exception e) {
messageId = R.string.snackbar_arcore_exception;
exception = e;
}

if (exception != null) {
snackbarHelper.showError(this, getString(messageId));
Log.e(TAG, "Exception creating session", exception);
return;
}

Config config = new Config(session);
session.configure(config);
}

try {
session.resume();
} catch (CameraNotAvailableException e) {
snackbarHelper.showError(this, getString(R.string.snackbar_camera_unavailable));
session = null;
return;
}
surfaceView.onResume();
displayRotationHelper.onResume();
}

@Override
public void onPause() {
super.onPause();
if (session != null) {
displayRotationHelper.onPause();
surfaceView.onPause();
session.pause();
}
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
if (!CameraPermissionHelper.hasCameraPermission(this)) {
Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG)
.show();
if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
CameraPermissionHelper.launchPermissionSettings(this);
}
finish();
}
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
FullScreenHelper.setFullScreenOnWindowFocusChanged(this, hasFocus);
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

try {
backgroundRenderer.createOnGlThread(/*context=*/ this);
planeRenderer.createOnGlThread(/*context=*/ this, "models/trigrid.png");
pointCloudRenderer.createOnGlThread(/*context=*/ this);

virtualObject.createOnGlThread(/*context=*/ this, "models/andy.obj", "models/andy.png");
virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);

virtualObjectShadow.createOnGlThread(
/*context=*/ this, "models/andy_shadow.obj", "models/andy_shadow.png");
virtualObjectShadow.setBlendMode(BlendMode.Shadow);
virtualObjectShadow.setMaterialProperties(1.0f, 0.0f, 0.0f, 1.0f);
} catch (IOException ex) {
Log.e(TAG, "Failed to read an asset file", ex);
}
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
displayRotationHelper.onSurfaceChanged(width, height);
GLES20.glViewport(0, 0, width, height);
}

@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

if (session == null) {
return;
}
displayRotationHelper.updateSessionIfNeeded(session);

try {
session.setCameraTextureName(backgroundRenderer.getTextureId());
Frame frame = session.update();
Camera camera = frame.getCamera();
TrackingState cameraTrackingState = camera.getTrackingState();

handleTapOnDraw(cameraTrackingState, frame);

backgroundRenderer.draw(frame);

if (cameraTrackingState == TrackingState.PAUSED) {
return;
}

camera.getProjectionMatrix(projectionMatrix, 0, 0.1f, 100.0f);
camera.getViewMatrix(viewMatrix, 0);

PointCloud pointCloud = frame.acquirePointCloud();
pointCloudRenderer.update(pointCloud);
pointCloudRenderer.draw(viewMatrix, projectionMatrix);

pointCloud.release();

planeRenderer.drawPlanes(
session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projectionMatrix);

boolean shouldDrawAnchor = false;
synchronized (singleTapAnchorLock) {
if (anchor != null && anchor.getTrackingState() == TrackingState.TRACKING) {
frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);

anchor.getPose().toMatrix(anchorMatrix, 0);
shouldDrawAnchor = true;
}
}
if (shouldDrawAnchor) {
float scaleFactor = 1.0f;
frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);

virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObjectShadow.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewMatrix, projectionMatrix, colorCorrectionRgba);
virtualObjectShadow.draw(viewMatrix, projectionMatrix, colorCorrectionRgba);
}
} catch (Throwable t) {
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}

@GuardedBy("singleTapAnchorLock")
private void setNewAnchor(@Nullable Anchor newAnchor) {
if (anchor != null) {
anchor.detach();
}
anchor = newAnchor;
}
}

……这里有一个 StorageManager类:
package com.google.ar.core.codelab.cloudanchor;

class StorageManager {

interface CloudAnchorIdListener {
void onCloudAnchorIdAvailable(String cloudAnchorId);
}

interface ShortCodeListener {
void onShortCodeAvailable(Integer shortCode);
}

private static final String TAG = StorageManager.class.getName();
private static final String KEY_ROOT_DIR = "shared_anchor_codelab_root";
private static final String KEY_NEXT_SHORT_CODE = "next_short_code";
private static final String KEY_PREFIX = "anchor;";
private static final int INITIAL_SHORT_CODE = 142;
private final DatabaseReference rootRef;

StorageManager(Context context) {
FirebaseApp firebaseApp = FirebaseApp.initializeApp(context);
rootRef = FirebaseDatabase.getInstance(firebaseApp).getReference().child(KEY_ROOT_DIR);
DatabaseReference.goOnline();
}

void nextShortCode(ShortCodeListener listener) {
rootRef
.child(KEY_NEXT_SHORT_CODE)
.runTransaction(
new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData currentData) {
Integer shortCode = currentData.getValue(Integer.class);
if (shortCode == null) {
shortCode = INITIAL_SHORT_CODE - 1;
}
currentData.setValue(shortCode + 1);
return Transaction.success(currentData);
}

@Override
public void onComplete(
DatabaseError error, boolean committed, DataSnapshot currentData) {
if (!committed) {
Log.e(TAG, "Firebase Error", error.toException());
listener.onShortCodeAvailable(null);
} else {
listener.onShortCodeAvailable(currentData.getValue(Integer.class));
}
}
});
}

void storeUsingShortCode(int shortCode, String cloudAnchorId) {
rootRef.child(KEY_PREFIX + shortCode).setValue(cloudAnchorId);
}

void getCloudAnchorID(int shortCode, CloudAnchorIdListener listener) {
rootRef
.child(KEY_PREFIX + shortCode)
.addListenerForSingleValueEvent(
new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
listener.onCloudAnchorIdAvailable(String.valueOf(dataSnapshot.getValue()));
}

@Override
public void onCancelled(DatabaseError error) {
Log.e(TAG, "The database operation for getCloudAnchorID was cancelled.",
error.toException());
listener.onCloudAnchorIdAvailable(null);
}
});
}
}

希望这有帮助。

关于java - 如何使用Cloud Anchor和Sceneform构建ARCore多人游戏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53002015/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com