Xây dựng và tương tác với một cảnh

Trang này chứa các mẹo phổ biến để xây dựng Scene và tương tác với trang đó.

Kết xuất cảnh không có thực tế tăng cường

Lớp SceneView cho phép bạn hiển thị cảnh 3D mà không cần sử dụng máy ảnh của thiết bị hoặc phiên thực tế tăng cường (AR). Điều này sẽ hữu ích khi xem trước đối tượng 3D trong ứng dụng của bạn mà không có AR hoặc cung cấp chức năng thay thế trên những thiết bị không hỗ trợ AR.

Theo mặc định, SceneView không hiển thị hình ảnh từ máy ảnh thực tế tăng cường (AR) và sử dụng nền đen. Để thay đổi màu nền, bạn có thể gọi view.setBackgroundColor() hoặc xác định màu nền trong bố cục như dưới đây:

<com.google.ar.sceneform.SceneView
    android:id="@+id/scene_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/deep_teal"/>

Nút Camera của cảnh được đặt ở điểm gốc (vị trí 0,0,0) và hướng về phía trước (hướng 0,0,-1). Vì vị trí và chế độ xoay của máy ảnh không gắn với tính năng theo dõi chuyển động thực tế tăng cường (AR), nên bạn có thể đặt lại vị trí hoặc tạo ảnh động cho bất kỳ nút nào khác.

Camera camera = sceneView.getScene().getCamera();
camera.setLocalRotation(Quaternion.axisAngle(Vector3.right(), -30.0f));

Lượt tương tác

Xử lý thao tác chạm của người dùng

Khi người dùng chạm vào màn hình, Sceneform sẽ truyền sự kiện chạm đến các trình xử lý và trình nghe sự kiện được đính kèm vào các nút và cảnh. Hành vi này tương tự như cách sự kiện nhấn truyền đến các thành phần hiển thị và nhóm thành phần hiển thị trong Android. Sau đây là thứ tự truyền bá:

  1. Sự kiện này được gửi tới mọi trình nghe được thêm vào scene.addOnPeekTouchListener().

    Điều này tương tự như viewGroup.intercept(), ngoại trừ việc trình nghe chạm chạm không thể xử lý sự kiện.

  2. Sự kiện được chuyển đến nút đầu tiên mà tia giao nhau với.

    • Nút này có thể sử dụng sự kiện bằng cách xác định một phương thức onTouchEvent() trả về true.
    • Nếu phương thức onTouchEvent() trả về false hoặc không có trình nghe nào được xác định, thì sự kiện sẽ được truyền đến cấp độ gốc của nút. Quá trình này sẽ tiếp tục cho đến khi sự kiện được xử lý hoặc đạt đến cảnh.
  3. Cuối cùng, nếu không có trình nghe nào sử dụng sự kiện này, thì sự kiện sẽ được chuyển cho scene.onTouchListener().

Phát hiện cử chỉ

ArFragment đã tích hợp sẵn tính năng hỗ trợ các cử chỉ nhấn (lựa chọn), kéo (di chuyển), chụm (mở rộng) và xoay (xoay).

Ví dụ: xem HelloSceneformActivity.java trong ứng dụng mẫu HelloSceneform.

Tạo các nút tùy chỉnh

Tương tự như việc tạo thành phần hiển thị Android tuỳ chỉnh, bạn có thể tạo các nút tuỳ chỉnh bằng cách phân lớp con Node. Dưới đây là một số tình huống mà bạn nên tạo nút tuỳ chỉnh:

  • Bạn muốn truy cập vào các sự kiện trong vòng đời của nút, chẳng hạn như onUpdate(), onActivateonDeactivate().
  • Bạn muốn tạo một nút bao gồm một nhóm các nút.
  • Bạn đang sao chép nhiều mã và có thể đưa mã đó vào một lớp con.

Để biết ví dụ, hãy xem Planet.java trong ứng dụng mẫu Hệ thống năng lượng mặt trời.

Tạo ảnh động cho các nút

Có hai cách để tạo ảnh động cho nút:

Tạo ảnh động bằng ObjectAnimator

Dưới đây là ví dụ tạo ảnh động đến cường độ của tiêu điểm:

final int durationInMilliseconds = 1000;
final float minimumIntensity = 1000.0f;
final float maximumIntensity = 3000.0f;
ValueAnimator intensityAnimator =
    ObjectAnimator.ofFloat(
        spotlightNode.getLight(), "intensity", minimumIntensity, maximumIntensity);
intensityAnimator.setDuration(durationInMilliseconds);
intensityAnimator.setRepeatCount(ValueAnimator.INFINITE);
intensityAnimator.setRepeatMode(ValueAnimator.REVERSE);
intensityAnimator.start();

Để biết thêm thông tin, hãy xem phần Tạo ảnh động bằng ObjectAnimator.

Tạo ảnh động trong onUpdate

Ghi đè onUpdate() của nút để tạo hiệu ứng chuyển khung hình thành khung. Ví dụ sau đây, từ Planet.java trong ứng dụng mẫu Hệ thống năng lượng mặt trời, điều chỉnh thẻ thông tin mỗi khung hình để hiển thị với người dùng, ngay cả khi hành tinh này xoay.

@Override
public void onUpdate(FrameTime frameTime) {
  Vector3 cameraPosition = getScene().getCamera().getWorldPosition();
  Vector3 cardPosition = infoCard.getWorldPosition();
  Vector3 direction = Vector3.subtract(cameraPosition, cardPosition);
  Quaternion lookRotation = Quaternion.lookRotation(direction, Vector3.up());
  infoCard.setWorldRotation(lookRotation);
}

Thêm đèn

Bạn có thể đính kèm Lights vào bất kỳ nút nào trong cảnh. Theo mặc định, mọi cảnh Cảnh đều bao gồm một nút Sun, được gắn đèn định hướng.

Bạn có thể chỉnh sửa mặt trời hoặc thêm đèn của riêng mình vào một cảnh. Ví dụ sau đây sẽ giúp bạn thêm tiêu điểm:

Light spotLightYellow =
    Light.builder(this, Light.Type.FOCUSED_SPOTLIGHT)
        .setColor(new Color(android.graphics.Color.YELLOW))
        .setShadowCastingEnabled(true)
        .build();

Sau đó, hãy gọi setLight() để đính kèm tệp đó vào nút.

Tùy chỉnh hình ảnh mặt phẳng

Theo mặc định, cảnh có PlaneRenderer làm nổi bật Planes khi được phát hiện bằng ARCore. Thông báo sẽ có dạng như sau:

Bạn có thể sửa đổi chất liệu và hoạ tiết mặc định dùng để kết xuất các mặt phẳng được phát hiện. Sau đây là cách thay đổi hoạ tiết:

Texture.Sampler sampler =
        Texture.Sampler.builder()
                .setMinFilter(Texture.Sampler.MinFilter.LINEAR)
                .setWrapMode(Texture.Sampler.WrapMode.REPEAT)
                .build();

// R.drawable.custom_texture is a .png file in src/main/res/drawable
Texture.builder()
        .setSource(this, R.drawable.custom_texture)
        .setSampler(sampler)
        .build()
        .thenAccept(texture -> {
          arSceneView.getPlaneRenderer()
                  .getMaterial().thenAccept(material ->
                  material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture));
        });

Bóng

Bóng đổ khiến kết xuất đồ hoạ xuất hiện trên mặt đất trên thế giới và giúp người dùng cảm nhận được chiều sâu và không gian.

Trong Cảnh, có những đối tượng có thể truyền bóng và các đối tượng có thể nhận được bóng.

  • LightsRenderables có thể truyền bóng

    Theo mặc định, tính năng truyền bóng được bật trên mặt trời nhưng không bật được cho đèn. Hãy gọi setShadowCastingEnabled() để bật tính năng này.

  • RenderablesPlaneRenderer có thể nhận được bóng.

    Theo mặc định, tính năng nhận bóng đã được bật. Hãy gọi cho setShadowReceiver() để tắt.

Nếu một trình kết xuất có thể truyền và nhận bóng, thì quá trình này có thể tự truyền bóng.