<转>Generic Data Access Objects

本文详细介绍了DAO(Data Access Object)的设计原则与实现方法,并通过使用Hibernate框架进行具体实例演示。探讨了不同场景下DAO接口及其实现方式,包括如何通过工厂模式、手动依赖注入等方式准备DAO组件。

Generic Data Access Objects
http://www.hibernate.org/328.html
...dao的设计,与数据库打交道都需要吧
 
The DAO interfaces
An implementation with Hibernate
Preparing DAOs with factories
Preparing DAOs with manual dependency injection
Preparing DAOs with lookup
Writing DAOs as managed EJB 3.0 components
This is a pattern for Data Access Objects with JDK 5.0, from the CaveatEmptor example application. It is also explained in the book Java Persistence with Hibernate. Two links you might find useful: Sessions and transactions and Open Session in View.

This time I based the DAO example on interfaces. Tools like Hibernate already provide database portability, so persistence layer portability shouldn't be a driving motivation for interfaces. However, DAO interfaces make sense in more complex applications, when several persistence services are encapsulate in one persistence layer. I'd say that you should use Hibernate (or Java Persistence APIs) directly in most cases, the best reason to use an additional DAO layer is higher abstraction (e.g. methods like getMaximumBid() instead of session.createQuery(...) repeated a dozen times).


The DAO interfaces
I use one interface per persistent entity, with a super interface for common CRUD functionality:

public interface GenericDAO {

    T findById(ID id, boolean lock);

    List findAll();

    List findByExample(T exampleInstance);

    T makePersistent(T entity);

    void makeTransient(T entity);
}

You can already see that this is going to be a pattern for a state-oriented data access API, with methods such as makePersistent() and makeTransient(). Furthermore, to implement a DAO you have to provide a type and an identifier argument. As for most ORM solutions, identifier types have to be serializable.

The DAO interface for a particular entity extends the generic interface and provides the type arguments:

public interface ItemDAO extends GenericDAO {

    public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
    public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";

    Bid getMaxBid(Long itemId);
    Bid getMinBid(Long itemId);

}

We basically separate generic CRUD operations and actual business-related data access operations from each other. (Ignore the named query constants for now, they are convenient if you use annotations.) However, even if only CRUD operations are needed for a particular entity, you should still write an interface for it, even it it is going to be empty. It is important to use a concrete DAO in your controller code, otherwise you will face some refactoring once you have to introduce specific data access operations for this entity.


An implementation with Hibernate
An implementation of the interfaces could be done with any state-management capable persistence service. First, the generic CRUD implementation with Hibernate:

public abstract class GenericHibernateDAO
        implements GenericDAO {

    private Class persistentClass;
    private Session session;

    public GenericHibernateDAO() {
        this.persistentClass = (Class) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
     }

    @SuppressWarnings("unchecked")
    public void setSession(Session s) {
        this.session = s;
    }

    protected Session getSession() {
        if (session == null)
            throw new IllegalStateException("Session has not been set on DAO before usage");
        return session;
    }

    public Class getPersistentClass() {
        return persistentClass;
    }

    @SuppressWarnings("unchecked")
    public T findById(ID id, boolean lock) {
        T entity;
        if (lock)
            entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
        else
            entity = (T) getSession().load(getPersistentClass(), id);

        return entity;
    }

    @SuppressWarnings("unchecked")
    public List findAll() {
        return findByCriteria();
    }

    @SuppressWarnings("unchecked")
    public List findByExample(T exampleInstance, String[] excludeProperty) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        Example example =  Example.create(exampleInstance);
        for (String exclude : excludeProperty) {
            example.excludeProperty(exclude);
        }
        crit.add(example);
        return crit.list();
    }

    @SuppressWarnings("unchecked")
    public T makePersistent(T entity) {
        getSession().saveOrUpdate(entity);
        return entity;
    }

    public void makeTransient(T entity) {
        getSession().delete(entity);
    }

    public void flush() {
        getSession().flush();
    }

    public void clear() {
        getSession().clear();
    }

    /**
     * Use this inside subclasses as a convenience method.
     */
    @SuppressWarnings("unchecked")
    protected List findByCriteria(Criterion... criterion) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterion) {
            crit.add(c);
        }
        return crit.list();
   }

}

There are some interesting things in this implementation. First, it clearly needs a Session to work, provided with setter injection. You could also use constructor injection. How you set the Session and what scope this Session has is of no concern to the actual DAO implementation. A DAO should not control transactions or the Session scope.

We need to suppress a few compile-time warnings about unchecked casts, because Hibernate's interfaces are JDK 1.4 only. What follows are the implementations of the generic CRUD operations, quite straightforward. The last method is quite nice, using another JDK 5.0 feature, varargs. It helps us to build Criteria queries in concrete entity DAOs. This is an example of a concrete DAO that extends the generic DAO implementation for Hibernate:

public class ItemDAOHibernate
        extends     GenericHibernateDAO
        implements  ItemDAO {

    public Bid getMaxBid(Long itemId) {
        Query q = getSession().getNamedQuery(ItemDAO.QUERY_MAXBID);
        q.setParameter("itemid", itemId);
        return (Bid) q.uniqueResult();
    }

    public Bid getMinBid(Long itemId) {
        Query q = getSession().getNamedQuery(ItemDAO.QUERY_MINBID);
        q.setParameter("itemid", itemId);
        return (Bid) q.uniqueResult();
    }

}

Another example which uses the findByCriteria() method of the superclass with variable arguments:

public class CategoryDAOHibernate
        extends     GenericHibernateDAO
        implements  CategoryDAO {

    public Collection findAll(boolean onlyRootCategories) {
        if (onlyRootCategories)
            return findByCriteria( Expression.isNull("parent") );
        else
            return findAll();
    }
}


Preparing DAOs with factories
We could bring it all together in a DAO factory, which not only sets the Session when a DAO is constructed but also contains nested classes to implement CRUD-only DAOs with no business-related operations:

public class HibernateDAOFactory extends DAOFactory {

    public ItemDAO getItemDAO() {
        return (ItemDAO)instantiateDAO(ItemDAOHibernate.class);
    }

    public CategoryDAO getCategoryDAO() {
        return (CategoryDAO)instantiateDAO(CategoryDAOHibernate.class);
    }

    public CommentDAO getCommentDAO() {
        return (CommentDAO)instantiateDAO(CommentDAOHibernate.class);
    }

    public ShipmentDAO getShipmentDAO() {
        return (ShipmentDAO)instantiateDAO(ShipmentDAOHibernate.class);
    }

    private GenericHibernateDAO instantiateDAO(Class daoClass) {
        try {
            GenericHibernateDAO dao = (GenericHibernateDAO)daoClass.newInstance();
            dao.setSession(getCurrentSession());
            return dao;
        } catch (Exception ex) {
            throw new RuntimeException("Can not instantiate DAO: " + daoClass, ex);
        }
    }

    // You could override this if you don't want HibernateUtil for lookup
    protected Session getCurrentSession() {
        return HibernateUtil.getSessionFactory().getCurrentSession();
    }

    // Inline concrete DAO implementations with no business-related data access methods.
    // If we use public static nested classes, we can centralize all of them in one source file.

    public static class CommentDAOHibernate
            extends GenericHibernateDAO
            implements CommentDAO {}

    public static class ShipmentDAOHibernate
            extends GenericHibernateDAO
            implements ShipmentDAO {}

}

This concrete factory for Hibernate DAOs extends the abstract factory, which is the interface we'll use in application code:

public abstract class DAOFactory {

    /**
     * Creates a standalone DAOFactory that returns unmanaged DAO
     * beans for use in any environment Hibernate has been configured
     * for. Uses HibernateUtil/SessionFactory and Hibernate context
     * propagation (CurrentSessionContext), thread-bound or transaction-bound,
     * and transaction scoped.
     */
    public static final Class HIBERNATE = org.hibernate.ce.auction.dao.hibernate.HibernateDAOFactory.class;

    /**
     * Factory method for instantiation of concrete factories.
     */
    public static DAOFactory instance(Class factory) {
        try {
            return (DAOFactory)factory.newInstance();
        } catch (Exception ex) {
            throw new RuntimeException("Couldn't create DAOFactory: " + factory);
        }
    }

    // Add your DAO interfaces here
    public abstract ItemDAO getItemDAO();
    public abstract CategoryDAO getCategoryDAO();
    public abstract CommentDAO getCommentDAO();
    public abstract ShipmentDAO getShipmentDAO();

}

Note that this factory example is suitable for persistence layers which are primarily implemented with a single persistence service, such as Hibernate or EJB 3.0 persistence. If you have to mix persistence APIs, for example, Hibernate and plain JDBC, the pattern changes slightly. Keep in mind that you can also call session.connection() inside a Hibernate-specific DAO, or use one of the many bulk operation/SQL support options in Hibernate 3.1 to avoid plain JDBC.

Finally, this is how data access now looks like in controller/command handler code (pick whatever transaction demarcation strategy you like, the DAO code doesn't change):

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {

    // JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
    // JTA: utx.begin();

    // Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();

    DAOFactory factory = DAOFactory.instance(DAOFactory.HIBERNATE);
    ItemDAO itemDAO = factory.getItemDAO();
    UserDAO userDAO = factory.getUserDAO();

    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Bid currentMinBid = itemDAO.getMinBid(itemId);

    Item item = itemDAO.findById(itemId, true);

    newBid = item.placeBid(userDAO.findById(userId, false),
                            bidAmount,
                            currentMaxBid,
                            currentMinBid);

    // JTA: utx.commit(); // Don't forget exception handling

    // Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling

}

The database transaction, either JTA or direct JDBC, is started and committed in an interceptor that runs for every execute(), following the Open Session in View pattern. You can use AOP for this or any kind of interceptor that can be wrapped around a method call, see Session handling with AOP.


Preparing DAOs with manual dependency injection
You don't need to write the factories. You can as well just do this:

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {

    // JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
    // JTA: utx.begin();

    // Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();

    ItemDAOHibernate itemDAO = new ItemDAOHibernate();
    itemDAO.setSession(HibernateUtil.getSessionFactory().getCurrentSession());

    UserDAOHibernate userDAO = new UserDAOHibernate();
    userDAO.setSession(HibernateUtil.getSessionFactory().getCurrentSession());

    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Bid currentMinBid = itemDAO.getMinBid(itemId);

    Item item = itemDAO.findById(itemId, true);

    newBid = item.placeBid(userDAO.findById(userId, false),
                            bidAmount,
                            currentMaxBid,
                            currentMinBid);

    // JTA: utx.commit(); // Don't forget exception handling

    // Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling

}

The disadvantage here is that the implementation classes (i.e. ItemDAOHibernate and UserDAOHibernate) of the persistence layer are exposed to the client, the controller. Also, constructor injection of the current Session might be more appropriate.


Preparing DAOs with lookup
Alternatively, call HibernateUtil.getSessionFactory().getCurrentSession() as a fallback, if the client didn't provide a Session when the DAO was constructed:

public abstract class GenericHibernateDAO
        implements GenericDAO {

    private Class persistentClass;
    private Session session;

    public GenericHibernateDAO() {
        this.persistentClass = (Class) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
   }

   public void setSession(Session session) {
        this.session = session;
   }

   protected void getSession() {
       if (session == null)
           session = HibernateUtil.getSessionFactory().getCurrentSession();
       return session;
   }

...

The controller now uses these stateless data access objects through direct instantiation:

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {

    // JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
    // JTA: utx.begin();

    // Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();

    ItemDAO itemDAO = new ItemDAOHibernate();
    UserDAO userDAO = new UserDAOHibernate();

    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Bid currentMinBid = itemDAO.getMinBid(itemId);

    Item item = itemDAO.findById(itemId, true);

    newBid = item.placeBid(userDAO.findById(userId, false),
                            bidAmount,
                            currentMaxBid,
                            currentMinBid);

    // JTA: utx.commit(); // Don't forget exception handling

    // Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling

}

The only disadvantage of this very simple strategy is that the implementation classes (i.e. ItemDAOHibernate and UserDAOHibernate) of the persistence layer are again exposed to the client, the controller. You can still supply a custom Session if needed (integration test, etc).

Each of these methods (factories, manual injection, lookup) for setting the current Session and creating a DAO instance has advantages and drawbacks, use whatever you feel most comfortable with.

Naturally, the cleanest way is managed components and EJB 3.0 session beans:


Writing DAOs as managed EJB 3.0 components
Turn your DAO superclass into a base class for stateless session beans (all your concrete DAOs are then stateless EJBs, they already have a business interface). This is basically a single annotation which you could even move into an XML deployment descriptor if you like. You can then use dependency injection and get the "current" persistence context provided by the container:

@Stateless
public abstract class GenericHibernateDAO
        implements GenericDAO {

    private Class persistentClass;
   
    @PersistenceContext
    private EntityManager em;

    public GenericHibernateDAO() {
       setSession( (Session)em.getDelegate() );   
    }

...

You can then cast the delegate of an EntityManager to a Hibernate Session.

This only works if you use Hibernate as a Java Persistence provider, because the delegate is the Session API. In JBoss AS you could even get a Session injected directly. If you use a different Java Persistence provider, rely on the EntityManager API instead of Session. Now wire your DAOs into the controller, which is also a managed component:

@Stateless
public class ManageAuctionController implements ManageAuction {

    @EJB ItemDAO itemDAO;
    @EJB UserDAO userDAO;

    @TransactionAttribute(TransactionAttributeType.REQUIRED) // This is even the default
    public void execute() {

        Bid currentMaxBid = itemDAO.getMaxBid(itemId);
        Bid currentMinBid = itemDAO.getMinBid(itemId);

        Item item = itemDAO.findById(itemId, true);

        newBid = item.placeBid(userDAO.findById(userId, false),
                               bidAmount,
                               currentMaxBid,
                               currentMinBid);

    }
}

P.S. Credit has to be given to Eric Burke, who first posted the basics for this pattern on his blog. Unfortunately, not even the Google cache is available anymore.


  NEW COMMENT 

Granularity  10 Sep 2005, 05:14  anthony 
Christian said:The DAO interface for a particular entity, that's ok
but can sometimes think in term of package.

Just one word about granularity, for big projects based on more than
100 persistent classes, if your design is well done, you'll have
different packages of 10-30 classes for example.

Use cases should be "driven" by a root entity for each package, so you
can define one DAO interface per package.

Example: what if you have Order * -- 1 Client and you need to get the
orders of a particular client? One use case needs only the orders, one
other also need the client to be fetched. Do you code this in
ClientDAO or OrderDAO knowing that client is the best candidate to be
a root entity...
This one is very simple but think about complex HQL queries on a 4 or
6 levels object graph. The code is not so easy to add and if you have
a big development team, there is risk to duplicate the code.

But you need to be carefull when designing your packages...
 
 
interface vs. class  23 Apr 2006, 13:17  andrewHib 
Hi,

The interface ItemDAO has to methods that retun an object of the
Bid-class. What happens if some sort of persistence mechanism needs the
class to implement an interface(Bid implements Persistable, for
example), then you can′t return the Bid class. Wouldn′t it be better
here to program against an interface Bid?
 
 
I think i found an error in the interface GenericDAO  05 Sep 2006, 14:48  juliano7s 
The implementation of the method findByExample(T exampleInstance,
String[] excludeProperty) in GenericHibernateDAO class is different from
its definition in the interface GenericDAO.

I tried to create a ItemDAO interface and ItemDAOHibernate class and got
the error
"The type ItemDAOHibernate must implement the inherited abstract method
 GenericDAO.findByExample(Item)"

Should you add the parameter to the interface GenericDAO or remove the
parameter from the GenericHibernateDAO class?
 
 
Re: I think i found an error...  29 Oct 2006, 15:02  kousen 
In case no one has answered this, check the Caveat Emptor source code. 
In there, they add the additional parameter to the findByExample
method.  As you noted, that takes care of any compilation issues.

They've also made another adjustment to the pattern as implemented by
CE.  Apparently they weren't happy with setting the persistentClass
attribute by reflection.  Instead, the latest code (HiA-SE3.1alpha5)
uses the constructor to set the class.  In the associated factory all
of the individual DAO classes call super with the class references as
an argument.
 
 
Transaction Management in DAOs  30 Oct 2006, 06:25  prashantbasawa 
Using transaction management outside the DAO is not a good idea. It
should be done inside the DAO itself. We can use a Strategy Pattern to
plug-in the transactional behaviour into the DAOs.

Declare a interface as

public interface TransactionStrategy {
   public void beginTransaction();
   public void commit();
}

Have an implementor like,

public class DefaultTransactionStrategy {
   public void beginTransaction(){
      HibernateUtil.getSessionFactory().getCurrentSession()
                                    .beginTransaction();
   }

   public void commit(){
      HibernateUtil.getSessionFactory().getCurrentSession()
                                    .commit();
   }
}

Now in the DAOs, you can have a instance variable declared for the
transaction strategy. Like

public class xyzDAO{
   private TransactionStrategy transaction = null;
  
   public xyzDAO(){
      transaction = new DefaultTransactionStrategy();
   }

   public void insert(Object obj){
      transaction.beginTransaction();
     
      // Do some work
      session.load(...);
      session.persist(...);

      transaction.commit();
   }

   public void setTransactionStrategy(TransactionStrategy strategy){
      transaction = strategy;
   }
}

Thus we can move the transaction management code to a strategy class
which will be used inside the DAOs.

In future, if you want to switch to a JTA Transaction, then you can
create a new transaction strategy for the same.

Also these strategies can be set to DAOs in
HibernateDaoFactory.instantiateDAO(Class clazz), where we have set the
session. Also, we can take the strategy info from a property file as
well. So based on a property in the property file, you can create a
strategy and set it to the DAOs.

Note: Strategy class can also accept the session through its constructor
if you define one which accepts it.
 

================以上是正文,建议看原文链接内容=====================
 
Re: Transaction Management in DAOs  30 Oct 2006, 07:28  prashantbasawa 
>Note: Strategy class can also accept the session through its constructor
>if you define one which accepts it.

Please ignore my "Note" which I wrote at the bottom of my topic.
 
 
Keep it simple  31 Oct 2006, 00:25  christian 
Of course, DAOs should not include programmatic transaction demarcation,
no matter which pattern-of-the-day you use to hide it.

At most, a DAO _can_ require a transaction context for particular methods.
This is best expressed with metadata, e.g. EJB 3.0 annotations.
 
 
Re: Transaction Management in DAOs  03 Nov 2006, 08:29  bora.erbas 
30 Oct 2006 07:28, prashantbasawa wrote:

>>Note: Strategy class can also accept the session through its constructor
>>if you define one which accepts it.

>Please ignore my "Note" which I wrote at the bottom of my topic.

so you basically mean; for every operation like persisting an object,
there should be a transaction began and committed? right?
i thought this was not a good idea...
 
 
Re: Transaction Management in DAOs  13 Nov 2006, 02:12  prashantbasawa 
POST QUESTIONS ON THE FORUM! COMMENTS HERE SHOULD ADD VALUE TO THE
PAGE!On 03 Nov 2006 08:29, bora.erbas wrote:

>so you basically mean; for every operation like persisting an object,
>there should be a transaction began and committed? right?
>i thought this was not a good idea...

How many operations are you going to have for persisting ? Only 3,
INSERT, UPDATE and DELETE which you can implement in a super class. So
there is no replication of code. Only things that vary for DAOs are the
way you query (SELECT) the respective tables, I don't thing u should use
transaction begin-commit for selecting rows of data where u r not making
changes to database.

I hope this answers ur doubt.
 
 
Re: Transaction Management in DAOs  14 Nov 2006, 05:19  christian 
>way you query (SELECT) the respective tables, I don't thing u should use
>transaction begin-commit for selecting rows of data where u r not making
>changes to database.

Nonsense. Of course you want regualar ACID guarantees for reading data,
especially if you want repeatable read isolation. Stop promoting the same
old "I don't really know what auto-commit is" story.
 
 
Salto-db Generator  27 Feb 2007, 04:59  rflament 
Just to let you know I have written a tool that generates code from the
database and one of the plugin, called "EJB3AndHibernateDao" generates
code based on this pattern.

It generates a pojo or an ejb3 for each table, a generic dao for each
pojo and an hibernate implementation of this dao.

See http://salto-db.sourceforge.net/salto-db-generator/index.html and
http://salto-db.sourceforge.net/salto-db-generator/plugins/ejb3hibernatedao.html

Thanks a lot for this article.
 
 
Re: Salto-db Generator  19 Mar 2007, 09:54  rjain15 
POST QUESTIONS ON THE FORUM! COMMENTS HERE SHOULD ADD VALUE TO THE
PAGE!On 27 Feb 2007 04:59, rflament wrote:

>Just to let you know I have written a tool that generates code from the
>database and one of the plugin, called "EJB3AndHibernateDao" generates
>code based on this pattern.

>It generates a pojo or an ejb3 for each table, a generic dao for each
>pojo and an hibernate implementation of this dao.

>See http://salto-db.sourceforge.net/salto-db-generator/index.html and
>http://salto-db.sourceforge.net/salto-db-generator/plugins/ejb3hibernatedao.html

>Thanks a lot for this article.

rflament: This is the a very useful plugin. I was looking the DAO
Generation and SalTo-DB is a good fit. Keep up the good work.

rjain
 
 
Transaction scope  14 May 2007, 05:42  Olivier Billard 
>Using transaction management outside the DAO is not a good idea. It
should be done inside the DAO itself.

Since I did not read a valid answer to this statement, here I am :).
Transaction should NOT be open/closed inside DAO themselves. This is
really a really idea :

- in that case you cannot include several DAO calls into one transaction
(atomicity on several entities CRUD cannot be done). This is more than a
little bit restrictive !
- in a JTA transaction, you may not have the control on the transaction
scope, you may just "participate" to the transaction (see first point)

I think that these only 2 points make it obvious, not to call
transaction begin/commit in the DAOs themselves. Of course, more can be
added.
 

### 光流法C++源代码解析与应用 #### 光流法原理 光流法是一种在计算机视觉领域中用于追踪视频序列中运动物体的方法。它基于亮度不变性假设,即场景中的点在时间上保持相同的灰度值,从而通过分析连续帧之间的像素变化来估计运动方向和速度。在数学上,光流场可以表示为像素位置和时间的一阶导数,即Ex、Ey(空间梯度)和Et(时间梯度),它们共同构成光流方程的基础。 #### C++实现细节 在给定的C++源代码片段中,`calculate`函数负责计算光流场。该函数接收一个图像缓冲区`buf`作为输入,并初始化了几个关键变量:`Ex`、`Ey`和`Et`分别代表沿x轴、y轴和时间轴的像素强度变化;`gray1`和`gray2`用于存储当前帧和前一帧的平均灰度值;`u`则表示计算出的光流矢量大小。 #### 图像处理流程 1. **初始化和预处理**:`memset`函数被用来清零`opticalflow`数组,它将保存计算出的光流数据。同时,`output`数组被填充为白色,这通常用于可视化结果。 2. **灰度计算**:对每一像素点进行处理,计算其灰度值。这里采用的是RGB通道平均值的计算方法,将每个像素的R、G、B值相加后除以3,得到一个近似灰度值。此步骤确保了计算过程的鲁棒性和效率。 3. **光流向量计算**:通过比较当前帧和前一帧的灰度值,计算出每个像素点的Ex、Ey和Et值。这里值得注意的是,光流向量的大小`u`是通过`Et`除以`sqrt(Ex^2 + Ey^2)`得到的,再乘以10进行量化处理,以减少计算复杂度。 4. **结果存储与阈值处理**:计算出的光流值被存储在`opticalflow`数组中。如果`u`的绝对值超过10,则认为该点存在显著运动,因此在`output`数组中将对应位置标记为黑色,形成运动区域的可视化效果。 5. **状态更新**:通过`memcpy`函数将当前帧复制到`prevframe`中,为下一次迭代做准备。 #### 扩展应用:Lukas-Kanade算法 除了上述基础的光流计算外,代码还提到了Lukas-Kanade算法的应用。这是一种更高级的光流计算方法,能够提供更精确的运动估计。在`ImgOpticalFlow`函数中,通过调用`cvCalcOpticalFlowLK`函数实现了这一算法,该函数接受前一帧和当前帧的灰度图,以及窗口大小等参数,返回像素级别的光流场信息。 在实际应用中,光流法常用于目标跟踪、运动检测、视频压缩等领域。通过深入理解和优化光流算法,可以进一步提升视频分析的准确性和实时性能。 光流法及其C++实现是计算机视觉领域的一个重要组成部分,通过对连续帧间像素变化的精细分析,能够有效捕捉和理解动态场景中的运动信息
微信小程序作为腾讯推出的一种轻型应用形式,因其便捷性与高效性,已广泛应用于日常生活中。以下为该平台的主要特性及配套资源说明: 特性方面: 操作便捷,即开即用:用户通过微信内搜索或扫描二维码即可直接使用,无需额外下载安装,减少了对手机存储空间的占用,也简化了使用流程。 多端兼容,统一开发:该平台支持在多种操作系统与设备上运行,开发者无需针对不同平台进行重复适配,可在一个统一的环境中完成开发工作。 功能丰富,接口完善:平台提供了多样化的API接口,便于开发者实现如支付功能、用户身份验证及消息通知等多样化需求。 社交整合,传播高效:小程序深度嵌入微信生态,能有效利用社交关系链,促进用户之间的互动与传播。 开发成本低,周期短:相比传统应用程序,小程序的开发投入更少,开发周期更短,有助于企业快速实现产品上线。 资源内容: “微信小程序-项目源码-原生开发框架-含效果截图示例”这一资料包,提供了完整的项目源码,并基于原生开发方式构建,确保了代码的稳定性与可维护性。内容涵盖项目结构、页面设计、功能模块等关键部分,配有详细说明与注释,便于使用者迅速理解并掌握开发方法。此外,还附有多个实际运行效果的截图,帮助用户直观了解功能实现情况,评估其在实际应用中的表现与价值。该资源适用于前端开发人员、技术爱好者及希望拓展业务的机构,具有较高的参考与使用价值。欢迎查阅,助力小程序开发实践。资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- using UnityEngine; using System; using System.Collections.Generic; using System.IO; using System.Reflection; /// <summary> /// Helper class containing generic functions used throughout the UI library. /// </summary> static public class NGUITools { static AudioListener mListener; static bool mLoaded = false; static float mGlobalVolume = 1f; /// <summary> /// Globally accessible volume affecting all sounds played via NGUITools.PlaySound(). /// </summary> static public float soundVolume { get { if (!mLoaded) { mLoaded = true; mGlobalVolume = PlayerPrefs.GetFloat("Sound", 1f); } return mGlobalVolume; } set { if (mGlobalVolume != value) { mLoaded = true; mGlobalVolume = value; PlayerPrefs.SetFloat("Sound", value); } } } /// <summary> /// Helper function -- whether the disk access is allowed. /// </summary> static public bool fileAccess { get { return Application.platform != RuntimePlatform.WindowsWebPlayer && Application.platform != RuntimePlatform.OSXWebPlayer; } } /// <summary> /// Play the specified audio clip. /// </summary> static public AudioSource PlaySound (AudioClip clip) { return PlaySound(clip, 1f, 1f); } /// <summary> /// Play the specified audio clip with the specified volume. /// </summary> static public AudioSource PlaySound (AudioClip clip, float volume) { return PlaySound(clip, volume, 1f); } static float mLastTimestamp = 0f; static AudioClip mLastClip; /// <summary> /// Play the specified audio clip with the specified volume and pitch. /// </summary> static public AudioSource PlaySound (AudioClip clip, float volume, float pitch) { float time = Time.time; if (mLastClip == clip && mLastTimestamp + 0.1f > time) return null; mLastClip = clip; mLastTimestamp = time; volume *= soundVolume; if (clip != null && volume > 0.01f) { if (mListener == null || !NGUITools.GetActive(mListener)) { AudioListener[] listeners = GameObject.FindObjectsOfType(typeof(AudioListener)) as AudioListener[]; if (listeners != null) { for (int i = 0; i < listeners.Length; ++i) { if (NGUITools.GetActive(listeners[i])) { mListener = listeners[i]; break; } } } if (mListener == null) { Camera cam = Camera.main; if (cam == null) cam = GameObject.FindObjectOfType(typeof(Camera)) as Camera; if (cam != null) mListener = cam.gameObject.AddComponent<AudioListener>(); } } if (mListener != null && mListener.enabled && NGUITools.GetActive(mListener.gameObject)) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 AudioSource source = mListener.audio; #else AudioSource source = mListener.GetComponent<AudioSource>(); #endif if (source == null) source = mListener.gameObject.AddComponent<AudioSource>(); #if !UNITY_FLASH source.priority = 50; source.pitch = pitch; #endif source.PlayOneShot(clip, volume); return source; } } return null; } /// <summary> /// New WWW call can fail if the crossdomain policy doesn't check out. Exceptions suck. It's much more elegant to check for null instead. /// </summary> // static public WWW OpenURL (string url) // { //#if UNITY_FLASH // Debug.LogError("WWW is not yet implemented in Flash"); // return null; //#else // WWW www = null; // try { www = new WWW(url); } // catch (System.Exception ex) { Debug.LogError(ex.Message); } // return www; //#endif // } // /// <summary> // /// New WWW call can fail if the crossdomain policy doesn't check out. Exceptions suck. It's much more elegant to check for null instead. // /// </summary> // static public WWW OpenURL (string url, WWWForm form) // { // if (form == null) return OpenURL(url); //#if UNITY_FLASH // Debug.LogError("WWW is not yet implemented in Flash"); // return null; //#else // WWW www = null; // try { www = new WWW(url, form); } // catch (System.Exception ex) { Debug.LogError(ex != null ? ex.Message : "<null>"); } // return www; //#endif // } /// <summary> /// Same as Random.Range, but the returned value is between min and max, inclusive. /// Unity's Random.Range is less than max instead, unless min == max. /// This means Range(0,1) produces 0 instead of 0 or 1. That's unacceptable. /// </summary> static public int RandomRange (int min, int max) { if (min == max) return min; return UnityEngine.Random.Range(min, max + 1); } /// <summary> /// Returns the hierarchy of the object in a human-readable format. /// </summary> static public string GetHierarchy (GameObject obj) { if (obj == null) return ""; string path = obj.name; while (obj.transform.parent != null) { obj = obj.transform.parent.gameObject; path = obj.name + "\\" + path; } return path; } /// <summary> /// Find all active objects of specified type. /// </summary> static public T[] FindActive<T> () where T : Component { return GameObject.FindObjectsOfType(typeof(T)) as T[]; } /// <summary> /// Find the camera responsible for drawing the objects on the specified layer. /// </summary> static public Camera FindCameraForLayer (int layer) { int layerMask = 1 << layer; Camera cam; for (int i = 0; i < UICamera.list.size; ++i) { cam = UICamera.list.buffer[i].cachedCamera; if (cam && (cam.cullingMask & layerMask) != 0) return cam; } cam = Camera.main; if (cam && (cam.cullingMask & layerMask) != 0) return cam; #if UNITY_4_3 || UNITY_FLASH Camera[] cameras = NGUITools.FindActive<Camera>(); for (int i = 0, imax = cameras.Length; i < imax; ++i) #else Camera[] cameras = new Camera[Camera.allCamerasCount]; int camerasFound = Camera.GetAllCameras(cameras); for (int i = 0; i < camerasFound; ++i) #endif { cam = cameras[i]; if (cam && cam.enabled && (cam.cullingMask & layerMask) != 0) return cam; } return null; } /// <summary> /// Add a collider to the game object containing one or more widgets. /// </summary> static public void AddWidgetCollider (GameObject go) { AddWidgetCollider(go, false); } /// <summary> /// Add a collider to the game object containing one or more widgets. /// </summary> static public void AddWidgetCollider (GameObject go, bool considerInactive) { if (go != null) { // 3D collider Collider col = go.GetComponent<Collider>(); BoxCollider box = col as BoxCollider; if (box != null) { UpdateWidgetCollider(box, considerInactive); return; } // Is there already another collider present? If so, do nothing. if (col != null) return; // 2D collider BoxCollider2D box2 = go.GetComponent<BoxCollider2D>(); if (box2 != null) { UpdateWidgetCollider(box2, considerInactive); return; } UICamera ui = UICamera.FindCameraForLayer(go.layer); if (ui != null && (ui.eventType == UICamera.EventType.World_2D || ui.eventType == UICamera.EventType.UI_2D)) { box2 = go.AddComponent<BoxCollider2D>(); box2.isTrigger = true; #if UNITY_EDITOR UnityEditor.Undo.RegisterCreatedObjectUndo(box2, "Add Collider"); #endif UIWidget widget = go.GetComponent<UIWidget>(); if (widget != null) widget.autoResizeBoxCollider = true; UpdateWidgetCollider(box2, considerInactive); return; } else { box = go.AddComponent<BoxCollider>(); #if UNITY_EDITOR UnityEditor.Undo.RegisterCreatedObjectUndo(box, "Add Collider"); #endif box.isTrigger = true; UIWidget widget = go.GetComponent<UIWidget>(); if (widget != null) widget.autoResizeBoxCollider = true; UpdateWidgetCollider(box, considerInactive); } } return; } /// <summary> /// Adjust the widget's collider based on the depth of the widgets, as well as the widget's dimensions. /// </summary> static public void UpdateWidgetCollider (GameObject go) { UpdateWidgetCollider(go, false); } /// <summary> /// Adjust the widget's collider based on the depth of the widgets, as well as the widget's dimensions. /// </summary> static public void UpdateWidgetCollider (GameObject go, bool considerInactive) { if (go != null) { BoxCollider bc = go.GetComponent<BoxCollider>(); if (bc != null) { UpdateWidgetCollider(bc, considerInactive); return; } BoxCollider2D box2 = go.GetComponent<BoxCollider2D>(); if (box2 != null) UpdateWidgetCollider(box2, considerInactive); } } /// <summary> /// Adjust the widget's collider based on the depth of the widgets, as well as the widget's dimensions. /// </summary> static public void UpdateWidgetCollider (BoxCollider box, bool considerInactive) { if (box != null) { GameObject go = box.gameObject; UIWidget w = go.GetComponent<UIWidget>(); if (w != null) { Vector4 dr = w.drawRegion; if (dr.x != 0f || dr.y != 0f || dr.z != 1f || dr.w != 1f) { Vector4 region = w.drawingDimensions; box.center = new Vector3((region.x + region.z) * 0.5f, (region.y + region.w) * 0.5f); box.size = new Vector3(region.z - region.x, region.w - region.y); } else { Vector3[] corners = w.localCorners; box.center = Vector3.Lerp(corners[0], corners[2], 0.5f); box.size = corners[2] - corners[0]; } } else { Bounds b = NGUIMath.CalculateRelativeWidgetBounds(go.transform, considerInactive); box.center = b.center; box.size = new Vector3(b.size.x, b.size.y, 0f); } #if UNITY_EDITOR NGUITools.SetDirty(box); #endif } } /// <summary> /// Adjust the widget's collider based on the depth of the widgets, as well as the widget's dimensions. /// </summary> static public void UpdateWidgetCollider (BoxCollider2D box, bool considerInactive) { if (box != null) { GameObject go = box.gameObject; UIWidget w = go.GetComponent<UIWidget>(); if (w != null) { Vector3[] corners = w.localCorners; #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 box.center = Vector3.Lerp(corners[0], corners[2], 0.5f); #else box.offset = Vector3.Lerp(corners[0], corners[2], 0.5f); #endif box.size = corners[2] - corners[0]; } else { Bounds b = NGUIMath.CalculateRelativeWidgetBounds(go.transform, considerInactive); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 box.center = b.center; #else box.offset = b.center; #endif box.size = new Vector2(b.size.x, b.size.y); } #if UNITY_EDITOR NGUITools.SetDirty(box); #endif } } /// <summary> /// Helper function that returns the string name of the type. /// </summary> static public string GetTypeName<T> () { string s = typeof(T).ToString(); if (s.StartsWith("UI")) s = s.Substring(2); else if (s.StartsWith("UnityEngine.")) s = s.Substring(12); return s; } /// <summary> /// Helper function that returns the string name of the type. /// </summary> static public string GetTypeName (UnityEngine.Object obj) { if (obj == null) return "Null"; string s = obj.GetType().ToString(); if (s.StartsWith("UI")) s = s.Substring(2); else if (s.StartsWith("UnityEngine.")) s = s.Substring(12); return s; } /// <summary> /// Convenience method that works without warnings in both Unity 3 and 4. /// </summary> static public void RegisterUndo (UnityEngine.Object obj, string name) { #if UNITY_EDITOR UnityEditor.Undo.RecordObject(obj, name); NGUITools.SetDirty(obj); #endif } /// <summary> /// Convenience function that marks the specified object as dirty in the Unity Editor. /// </summary> static public void SetDirty (UnityEngine.Object obj) { #if UNITY_EDITOR if (obj) { //if (obj is Component) Debug.Log(NGUITools.GetHierarchy((obj as Component).gameObject), obj); //else if (obj is GameObject) Debug.Log(NGUITools.GetHierarchy(obj as GameObject), obj); //else Debug.Log("Hmm... " + obj.GetType(), obj); UnityEditor.EditorUtility.SetDirty(obj); } #endif } /// <summary> /// Add a new child game object. /// </summary> static public GameObject AddChild (GameObject parent) { return AddChild(parent, true); } /// <summary> /// Add a new child game object. /// </summary> static public GameObject AddChild (GameObject parent, bool undo) { GameObject go = new GameObject(); #if UNITY_EDITOR if (undo) UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Create Object"); #endif if (parent != null) { Transform t = go.transform; t.parent = parent.transform; t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; go.layer = parent.layer; } return go; } /// <summary> /// Instantiate an object and add it to the specified parent. /// </summary> static public GameObject AddChild (GameObject parent, GameObject prefab) { GameObject go = GameObject.Instantiate(prefab) as GameObject; #if UNITY_EDITOR UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Create Object"); #endif if (go != null && parent != null) { Transform t = go.transform; t.parent = parent.transform; t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; go.layer = parent.layer; } return go; } /// <summary> /// Calculate the game object's depth based on the widgets within, and also taking panel depth into consideration. /// </summary> static public int CalculateRaycastDepth (GameObject go) { UIWidget w = go.GetComponent<UIWidget>(); if (w != null) return w.raycastDepth; UIWidget[] widgets = go.GetComponentsInChildren<UIWidget>(); if (widgets.Length == 0) return 0; int depth = int.MaxValue; for (int i = 0, imax = widgets.Length; i < imax; ++i) { if (widgets[i].enabled) depth = Mathf.Min(depth, widgets[i].raycastDepth); } return depth; } /// <summary> /// Gathers all widgets and calculates the depth for the next widget. /// </summary> static public int CalculateNextDepth (GameObject go) { int depth = -1; UIWidget[] widgets = go.GetComponentsInChildren<UIWidget>(); for (int i = 0, imax = widgets.Length; i < imax; ++i) depth = Mathf.Max(depth, widgets[i].depth); return depth + 1; } /// <summary> /// Gathers all widgets and calculates the depth for the next widget. /// </summary> static public int CalculateNextDepth (GameObject go, bool ignoreChildrenWithColliders) { if (ignoreChildrenWithColliders) { int depth = -1; UIWidget[] widgets = go.GetComponentsInChildren<UIWidget>(); for (int i = 0, imax = widgets.Length; i < imax; ++i) { UIWidget w = widgets[i]; #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 if (w.cachedGameObject != go && (w.collider != null || w.GetComponent<Collider2D>() != null)) continue; #else if (w.cachedGameObject != go && (w.GetComponent<Collider>() != null || w.GetComponent<Collider2D>() != null)) continue; #endif depth = Mathf.Max(depth, w.depth); } return depth + 1; } return CalculateNextDepth(go); } /// <summary> /// Adjust the widgets' depth by the specified value. /// Returns '0' if nothing was adjusted, '1' if panels were adjusted, and '2' if widgets were adjusted. /// </summary> static public int AdjustDepth (GameObject go, int adjustment) { if (go != null) { UIPanel panel = go.GetComponent<UIPanel>(); if (panel != null) { UIPanel[] panels = go.GetComponentsInChildren<UIPanel>(true); for (int i = 0; i < panels.Length; ++i) { UIPanel p = panels[i]; #if UNITY_EDITOR RegisterUndo(p, "Depth Change"); #endif p.depth = p.depth + adjustment; } return 1; } else { panel = FindInParents<UIPanel>(go); if (panel == null) return 0; UIWidget[] widgets = go.GetComponentsInChildren<UIWidget>(true); for (int i = 0, imax = widgets.Length; i < imax; ++i) { UIWidget w = widgets[i]; if (w.panel != panel) continue; #if UNITY_EDITOR RegisterUndo(w, "Depth Change"); #endif w.depth = w.depth + adjustment; } return 2; } } return 0; } /// <summary> /// Bring all of the widgets on the specified object forward. /// </summary> static public void BringForward (GameObject go) { int val = AdjustDepth(go, 1000); if (val == 1) NormalizePanelDepths(); else if (val == 2) NormalizeWidgetDepths(); } /// <summary> /// Push all of the widgets on the specified object back, making them appear behind everything else. /// </summary> static public void PushBack (GameObject go) { int val = AdjustDepth(go, -1000); if (val == 1) NormalizePanelDepths(); else if (val == 2) NormalizeWidgetDepths(); } /// <summary> /// Normalize the depths of all the widgets and panels in the scene, making them start from 0 and remain in order. /// </summary> static public void NormalizeDepths () { NormalizeWidgetDepths(); NormalizePanelDepths(); } /// <summary> /// Normalize the depths of all the widgets in the scene, making them start from 0 and remain in order. /// </summary> static public void NormalizeWidgetDepths () { NormalizeWidgetDepths(FindActive<UIWidget>()); } /// <summary> /// Normalize the depths of all the widgets in the scene, making them start from 0 and remain in order. /// </summary> static public void NormalizeWidgetDepths (GameObject go) { NormalizeWidgetDepths(go.GetComponentsInChildren<UIWidget>()); } /// <summary> /// Normalize the depths of all the widgets in the scene, making them start from 0 and remain in order. /// </summary> static public void NormalizeWidgetDepths (UIWidget[] list) { int size = list.Length; if (size > 0) { Array.Sort(list, UIWidget.FullCompareFunc); int start = 0; int current = list[0].depth; for (int i = 0; i < size; ++i) { UIWidget w = list[i]; if (w.depth == current) { w.depth = start; } else { current = w.depth; w.depth = ++start; } } } } /// <summary> /// Normalize the depths of all the panels in the scene, making them start from 0 and remain in order. /// </summary> static public void NormalizePanelDepths () { UIPanel[] list = FindActive<UIPanel>(); int size = list.Length; if (size > 0) { Array.Sort(list, UIPanel.CompareFunc); int start = 0; int current = list[0].depth; for (int i = 0; i < size; ++i) { UIPanel p = list[i]; if (p.depth == current) { p.depth = start; } else { current = p.depth; p.depth = ++start; } } } } /// <summary> /// Create a new UI. /// </summary> static public UIPanel CreateUI (bool advanced3D) { return CreateUI(null, advanced3D, -1); } /// <summary> /// Create a new UI. /// </summary> static public UIPanel CreateUI (bool advanced3D, int layer) { return CreateUI(null, advanced3D, layer); } /// <summary> /// Create a new UI. /// </summary> static public UIPanel CreateUI (Transform trans, bool advanced3D, int layer) { // Find the existing UI Root UIRoot root = (trans != null) ? NGUITools.FindInParents<UIRoot>(trans.gameObject) : null; if (root == null && UIRoot.list.Count > 0) { foreach (UIRoot r in UIRoot.list) { if (r.gameObject.layer == layer) { root = r; break; } } } // Try to find an existing panel if (root == null) { for (int i = 0, imax = UIPanel.list.Count; i < imax; ++i) { UIPanel p = UIPanel.list[i]; GameObject go = p.gameObject; if (go.hideFlags == HideFlags.None && go.layer == layer) { trans.parent = p.transform; trans.localScale = Vector3.one; return p; } } } // If we are working with a different UI type, we need to treat it as a brand-new one instead if (root != null) { UICamera cam = root.GetComponentInChildren<UICamera>(); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 if (cam != null && cam.camera.isOrthoGraphic == advanced3D) #else if (cam != null && cam.GetComponent<Camera>().orthographic == advanced3D) #endif { trans = null; root = null; } } // If no root found, create one if (root == null) { GameObject go = NGUITools.AddChild(null, false); root = go.AddComponent<UIRoot>(); // Automatically find the layers if none were specified if (layer == -1) layer = LayerMask.NameToLayer("UI"); if (layer == -1) layer = LayerMask.NameToLayer("2D UI"); go.layer = layer; if (advanced3D) { go.name = "UI Root (3D)"; root.scalingStyle = UIRoot.Scaling.Constrained; } else { go.name = "UI Root"; root.scalingStyle = UIRoot.Scaling.Flexible; } } // Find the first panel UIPanel panel = root.GetComponentInChildren<UIPanel>(); if (panel == null) { // Find other active cameras in the scene Camera[] cameras = NGUITools.FindActive<Camera>(); float depth = -1f; bool colorCleared = false; int mask = (1 << root.gameObject.layer); for (int i = 0; i < cameras.Length; ++i) { Camera c = cameras[i]; // If the color is being cleared, we won't need to if (c.clearFlags == CameraClearFlags.Color || c.clearFlags == CameraClearFlags.Skybox) colorCleared = true; // Choose the maximum depth depth = Mathf.Max(depth, c.depth); // Make sure this camera can't see the UI c.cullingMask = (c.cullingMask & (~mask)); } // Create a camera that will draw the UI Camera cam = NGUITools.AddChild<Camera>(root.gameObject, false); cam.gameObject.AddComponent<UICamera>(); cam.clearFlags = colorCleared ? CameraClearFlags.Depth : CameraClearFlags.Color; cam.backgroundColor = Color.grey; cam.cullingMask = mask; cam.depth = depth + 1f; if (advanced3D) { cam.nearClipPlane = 0.1f; cam.farClipPlane = 4f; cam.transform.localPosition = new Vector3(0f, 0f, -700f); } else { cam.orthographic = true; cam.orthographicSize = 1; cam.nearClipPlane = -10; cam.farClipPlane = 10; } // Make sure there is an audio listener present AudioListener[] listeners = NGUITools.FindActive<AudioListener>(); if (listeners == null || listeners.Length == 0) cam.gameObject.AddComponent<AudioListener>(); // Add a panel to the root panel = root.gameObject.AddComponent<UIPanel>(); #if UNITY_EDITOR UnityEditor.Selection.activeGameObject = panel.gameObject; #endif } if (trans != null) { // Find the root object while (trans.parent != null) trans = trans.parent; if (NGUITools.IsChild(trans, panel.transform)) { // Odd hierarchy -- can't reparent panel = trans.gameObject.AddComponent<UIPanel>(); } else { // Reparent this root object to be a child of the panel trans.parent = panel.transform; trans.localScale = Vector3.one; trans.localPosition = Vector3.zero; SetChildLayer(panel.cachedTransform, panel.cachedGameObject.layer); } } return panel; } /// <summary> /// Helper function that recursively sets all children with widgets' game objects layers to the specified value. /// </summary> static public void SetChildLayer (Transform t, int layer) { for (int i = 0; i < t.childCount; ++i) { Transform child = t.GetChild(i); child.gameObject.layer = layer; SetChildLayer(child, layer); } } /// <summary> /// Add a child object to the specified parent and attaches the specified script to it. /// </summary> static public T AddChild<T> (GameObject parent) where T : Component { GameObject go = AddChild(parent); go.name = GetTypeName<T>(); return go.AddComponent<T>(); } /// <summary> /// Add a child object to the specified parent and attaches the specified script to it. /// </summary> static public T AddChild<T> (GameObject parent, bool undo) where T : Component { GameObject go = AddChild(parent, undo); go.name = GetTypeName<T>(); return go.AddComponent<T>(); } /// <summary> /// Add a new widget of specified type. /// </summary> static public T AddWidget<T> (GameObject go) where T : UIWidget { int depth = CalculateNextDepth(go); // Create the widget and place it above other widgets T widget = AddChild<T>(go); widget.width = 100; widget.height = 100; widget.depth = depth; return widget; } /// <summary> /// Add a new widget of specified type. /// </summary> static public T AddWidget<T> (GameObject go, int depth) where T : UIWidget { // Create the widget and place it above other widgets T widget = AddChild<T>(go); widget.width = 100; widget.height = 100; widget.depth = depth; return widget; } /// <summary> /// Add a sprite appropriate for the specified atlas sprite. /// It will be sliced if the sprite has an inner rect, and a regular sprite otherwise. /// </summary> static public UISprite AddSprite (GameObject go, UIAtlas atlas, string spriteName) { UISpriteData sp = (atlas != null) ? atlas.GetSprite(spriteName) : null; UISprite sprite = AddWidget<UISprite>(go); sprite.type = (sp == null || !sp.hasBorder) ? UISprite.Type.Simple : UISprite.Type.Sliced; sprite.atlas = atlas; sprite.spriteName = spriteName; return sprite; } /// <summary> /// Get the rootmost object of the specified game object. /// </summary> static public GameObject GetRoot (GameObject go) { Transform t = go.transform; for (; ; ) { Transform parent = t.parent; if (parent == null) break; t = parent; } return t.gameObject; } /// <summary> /// Finds the specified component on the game object or one of its parents. /// </summary> static public T FindInParents<T> (GameObject go) where T : Component { if (go == null) return null; // Commented out because apparently it causes Unity 4.5.3 to lag horribly: // http://www.tasharen.com/forum/index.php?topic=10882.0 //#if UNITY_4_3 #if UNITY_FLASH object comp = go.GetComponent<T>(); #else T comp = go.GetComponent<T>(); #endif if (comp == null) { Transform t = go.transform.parent; while (t != null && comp == null) { comp = t.gameObject.GetComponent<T>(); t = t.parent; } } #if UNITY_FLASH return (T)comp; #else return comp; #endif //#else // return go.GetComponentInParent<T>(); //#endif } /// <summary> /// Finds the specified component on the game object or one of its parents. /// </summary> static public T FindInParents<T> (Transform trans) where T : Component { if (trans == null) return null; #if UNITY_4_3 #if UNITY_FLASH object comp = trans.GetComponent<T>(); #else T comp = trans.GetComponent<T>(); #endif if (comp == null) { Transform t = trans.transform.parent; while (t != null && comp == null) { comp = t.gameObject.GetComponent<T>(); t = t.parent; } } #if UNITY_FLASH return (T)comp; #else return comp; #endif #else return trans.GetComponentInParent<T>(); #endif } /// <summary> /// Destroy the specified object, immediately if in edit mode. /// </summary> static public void Destroy (UnityEngine.Object obj) { if (obj != null) { if (obj is Transform) obj = (obj as Transform).gameObject; if (Application.isPlaying) { if (obj is GameObject) { GameObject go = obj as GameObject; go.transform.parent = null; } UnityEngine.Object.Destroy(obj); } else UnityEngine.Object.DestroyImmediate(obj); } } /// <summary> /// Destroy the specified object immediately, unless not in the editor, in which case the regular Destroy is used instead. /// </summary> static public void DestroyImmediate (UnityEngine.Object obj) { if (obj != null) { if (Application.isEditor) UnityEngine.Object.DestroyImmediate(obj); else UnityEngine.Object.Destroy(obj); } } /// <summary> /// Call the specified function on all objects in the scene. /// </summary> static public void Broadcast (string funcName) { GameObject[] gos = GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[]; for (int i = 0, imax = gos.Length; i < imax; ++i) gos[i].SendMessage(funcName, SendMessageOptions.DontRequireReceiver); } /// <summary> /// Call the specified function on all objects in the scene. /// </summary> static public void Broadcast (string funcName, object param) { GameObject[] gos = GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[]; for (int i = 0, imax = gos.Length; i < imax; ++i) gos[i].SendMessage(funcName, param, SendMessageOptions.DontRequireReceiver); } /// <summary> /// Determines whether the 'parent' contains a 'child' in its hierarchy. /// </summary> static public bool IsChild (Transform parent, Transform child) { if (parent == null || child == null) return false; while (child != null) { if (child == parent) return true; child = child.parent; } return false; } /// <summary> /// Activate the specified object and all of its children. /// </summary> static void Activate (Transform t) { Activate(t, false); } /// <summary> /// Activate the specified object and all of its children. /// </summary> static void Activate (Transform t, bool compatibilityMode) { SetActiveSelf(t.gameObject, true); if (compatibilityMode) { // If there is even a single enabled child, then we're using a Unity 4.0-based nested active state scheme. for (int i = 0, imax = t.childCount; i < imax; ++i) { Transform child = t.GetChild(i); if (child.gameObject.activeSelf) return; } // If this point is reached, then all the children are disabled, so we must be using a Unity 3.5-based active state scheme. for (int i = 0, imax = t.childCount; i < imax; ++i) { Transform child = t.GetChild(i); Activate(child, true); } } } /// <summary> /// Deactivate the specified object and all of its children. /// </summary> static void Deactivate (Transform t) { SetActiveSelf(t.gameObject, false); } /// <summary> /// SetActiveRecursively enables children before parents. This is a problem when a widget gets re-enabled /// and it tries to find a panel on its parent. /// </summary> static public void SetActive (GameObject go, bool state) { SetActive(go, state, true); } /// <summary> /// SetActiveRecursively enables children before parents. This is a problem when a widget gets re-enabled /// and it tries to find a panel on its parent. /// </summary> static public void SetActive (GameObject go, bool state, bool compatibilityMode) { if (go) { if (state) { Activate(go.transform, compatibilityMode); #if UNITY_EDITOR if (Application.isPlaying) #endif CallCreatePanel(go.transform); } else Deactivate(go.transform); } } /// <summary> /// Ensure that all widgets have had their panels created, forcing the update right away rather than on the following frame. /// </summary> [System.Diagnostics.DebuggerHidden] [System.Diagnostics.DebuggerStepThrough] static void CallCreatePanel (Transform t) { UIWidget w = t.GetComponent<UIWidget>(); if (w != null) w.CreatePanel(); for (int i = 0, imax = t.childCount; i < imax; ++i) CallCreatePanel(t.GetChild(i)); } /// <summary> /// Activate or deactivate children of the specified game object without changing the active state of the object itself. /// </summary> static public void SetActiveChildren (GameObject go, bool state) { Transform t = go.transform; if (state) { for (int i = 0, imax = t.childCount; i < imax; ++i) { Transform child = t.GetChild(i); Activate(child); } } else { for (int i = 0, imax = t.childCount; i < imax; ++i) { Transform child = t.GetChild(i); Deactivate(child); } } } /// <summary> /// Helper function that returns whether the specified MonoBehaviour is active. /// </summary> [System.Obsolete("Use NGUITools.GetActive instead")] static public bool IsActive (Behaviour mb) { return mb != null && mb.enabled && mb.gameObject.activeInHierarchy; } /// <summary> /// Helper function that returns whether the specified MonoBehaviour is active. /// </summary> [System.Diagnostics.DebuggerHidden] [System.Diagnostics.DebuggerStepThrough] static public bool GetActive (Behaviour mb) { return mb && mb.enabled && mb.gameObject.activeInHierarchy; } /// <summary> /// Unity4 has changed GameObject.active to GameObject.activeself. /// </summary> [System.Diagnostics.DebuggerHidden] [System.Diagnostics.DebuggerStepThrough] static public bool GetActive (GameObject go) { return go && go.activeInHierarchy; } /// <summary> /// Unity4 has changed GameObject.active to GameObject.SetActive. /// </summary> [System.Diagnostics.DebuggerHidden] [System.Diagnostics.DebuggerStepThrough] static public void SetActiveSelf (GameObject go, bool state) { go.SetActive(state); } /// <summary> /// Recursively set the game object's layer. /// </summary> static public void SetLayer (GameObject go, int layer) { go.layer = layer; Transform t = go.transform; for (int i = 0, imax = t.childCount; i < imax; ++i) { Transform child = t.GetChild(i); SetLayer(child.gameObject, layer); } } /// <summary> /// Helper function used to make the vector use integer numbers. /// </summary> static public Vector3 Round (Vector3 v) { v.x = Mathf.Round(v.x); v.y = Mathf.Round(v.y); v.z = Mathf.Round(v.z); return v; } /// <summary> /// Make the specified selection pixel-perfect. /// </summary> static public void MakePixelPerfect (Transform t) { UIWidget w = t.GetComponent<UIWidget>(); if (w != null) w.MakePixelPerfect(); if (t.GetComponent<UIAnchor>() == null && t.GetComponent<UIRoot>() == null) { #if UNITY_EDITOR RegisterUndo(t, "Make Pixel-Perfect"); #endif t.localPosition = Round(t.localPosition); t.localScale = Round(t.localScale); } // Recurse into children for (int i = 0, imax = t.childCount; i < imax; ++i) MakePixelPerfect(t.GetChild(i)); } /// <summary> /// Save the specified binary data into the specified file. /// </summary> static public bool Save (string fileName, byte[] bytes) { #if UNITY_WEBPLAYER || UNITY_FLASH || UNITY_METRO || UNITY_WP8 || UNITY_WP_8_1 return false; #else if (!NGUITools.fileAccess) return false; string path = ""; if (bytes == null) { if (File.Exists(path)) File.Delete(path); return true; } FileStream file = null; try { file = File.Create(path); } catch (System.Exception ex) { Debug.LogError(ex.Message); return false; } file.Write(bytes, 0, bytes.Length); file.Close(); return true; #endif } /// <summary> /// Load all binary data from the specified file. /// </summary> static public byte[] Load (string fileName) { #if UNITY_WEBPLAYER || UNITY_FLASH || UNITY_METRO || UNITY_WP8 || UNITY_WP_8_1 return null; #else if (!NGUITools.fileAccess) return null; string path = ""; if (File.Exists(path)) { return File.ReadAllBytes(path); } return null; #endif } /// <summary> /// Pre-multiply shaders result in a black outline if this operation is done in the shader. It's better to do it outside. /// </summary> static public Color ApplyPMA (Color c) { if (c.a != 1f) { c.r *= c.a; c.g *= c.a; c.b *= c.a; } return c; } /// <summary> /// Inform all widgets underneath the specified object that the parent has changed. /// </summary> static public void MarkParentAsChanged (GameObject go) { UIRect[] rects = go.GetComponentsInChildren<UIRect>(); for (int i = 0, imax = rects.Length; i < imax; ++i) rects[i].ParentHasChanged(); } /// <summary> /// Access to the clipboard via undocumented APIs. /// </summary> static public string clipboard { get { TextEditor te = new TextEditor(); te.Paste(); return te.content.text; } set { TextEditor te = new TextEditor(); te.content = new GUIContent(value); te.OnFocus(); te.Copy(); } } [System.Obsolete("Use NGUIText.EncodeColor instead")] static public string EncodeColor (Color c) { return NGUIText.EncodeColor24(c); } [System.Obsolete("Use NGUIText.ParseColor instead")] static public Color ParseColor (string text, int offset) { return NGUIText.ParseColor24(text, offset); } [System.Obsolete("Use NGUIText.StripSymbols instead")] static public string StripSymbols (string text) { return NGUIText.StripSymbols(text); } /// <summary> /// Extension for the game object that checks to see if the component already exists before adding a new one. /// If the component is already present it will be returned instead. /// </summary> static public T AddMissingComponent<T> (this GameObject go) where T : Component { #if UNITY_FLASH object comp = go.GetComponent<T>(); #else T comp = go.GetComponent<T>(); #endif if (comp == null) { #if UNITY_EDITOR if (!Application.isPlaying) RegisterUndo(go, "Add " + typeof(T)); #endif comp = go.AddComponent<T>(); } #if UNITY_FLASH return (T)comp; #else return comp; #endif } // Temporary variable to avoid GC allocation static Vector3[] mSides = new Vector3[4]; /// <summary> /// Get sides relative to the specified camera. The order is left, top, right, bottom. /// </summary> static public Vector3[] GetSides (this Camera cam) { return cam.GetSides(Mathf.Lerp(cam.nearClipPlane, cam.farClipPlane, 0.5f), null); } /// <summary> /// Get sides relative to the specified camera. The order is left, top, right, bottom. /// </summary> static public Vector3[] GetSides (this Camera cam, float depth) { return cam.GetSides(depth, null); } /// <summary> /// Get sides relative to the specified camera. The order is left, top, right, bottom. /// </summary> static public Vector3[] GetSides (this Camera cam, Transform relativeTo) { return cam.GetSides(Mathf.Lerp(cam.nearClipPlane, cam.farClipPlane, 0.5f), relativeTo); } /// <summary> /// Get sides relative to the specified camera. The order is left, top, right, bottom. /// </summary> static public Vector3[] GetSides (this Camera cam, float depth, Transform relativeTo) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 if (cam.isOrthoGraphic) #else if (cam.orthographic) #endif { float os = cam.orthographicSize; float x0 = -os; float x1 = os; float y0 = -os; float y1 = os; Rect rect = cam.rect; Vector2 size = screenSize; float aspect = size.x / size.y; aspect *= rect.width / rect.height; x0 *= aspect; x1 *= aspect; // We want to ignore the scale, as scale doesn't affect the camera's view region in Unity Transform t = cam.transform; Quaternion rot = t.rotation; Vector3 pos = t.position; int w = Mathf.RoundToInt(size.x); int h = Mathf.RoundToInt(size.y); if ((w & 1) == 1) pos.x -= 1f / size.x; if ((h & 1) == 1) pos.y += 1f / size.y; mSides[0] = rot * (new Vector3(x0, 0f, depth)) + pos; mSides[1] = rot * (new Vector3(0f, y1, depth)) + pos; mSides[2] = rot * (new Vector3(x1, 0f, depth)) + pos; mSides[3] = rot * (new Vector3(0f, y0, depth)) + pos; } else { mSides[0] = cam.ViewportToWorldPoint(new Vector3(0f, 0.5f, depth)); mSides[1] = cam.ViewportToWorldPoint(new Vector3(0.5f, 1f, depth)); mSides[2] = cam.ViewportToWorldPoint(new Vector3(1f, 0.5f, depth)); mSides[3] = cam.ViewportToWorldPoint(new Vector3(0.5f, 0f, depth)); } if (relativeTo != null) { for (int i = 0; i < 4; ++i) mSides[i] = relativeTo.InverseTransformPoint(mSides[i]); } return mSides; } /// <summary> /// Get the camera's world-space corners. The order is bottom-left, top-left, top-right, bottom-right. /// </summary> static public Vector3[] GetWorldCorners (this Camera cam) { float depth = Mathf.Lerp(cam.nearClipPlane, cam.farClipPlane, 0.5f); return cam.GetWorldCorners(depth, null); } /// <summary> /// Get the camera's world-space corners. The order is bottom-left, top-left, top-right, bottom-right. /// </summary> static public Vector3[] GetWorldCorners (this Camera cam, float depth) { return cam.GetWorldCorners(depth, null); } /// <summary> /// Get the camera's world-space corners. The order is bottom-left, top-left, top-right, bottom-right. /// </summary> static public Vector3[] GetWorldCorners (this Camera cam, Transform relativeTo) { return cam.GetWorldCorners(Mathf.Lerp(cam.nearClipPlane, cam.farClipPlane, 0.5f), relativeTo); } /// <summary> /// Get the camera's world-space corners. The order is bottom-left, top-left, top-right, bottom-right. /// </summary> static public Vector3[] GetWorldCorners (this Camera cam, float depth, Transform relativeTo) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 if (cam.isOrthoGraphic) #else if (cam.orthographic) #endif { float os = cam.orthographicSize; float x0 = -os; float x1 = os; float y0 = -os; float y1 = os; Rect rect = cam.rect; Vector2 size = screenSize; float aspect = size.x / size.y; aspect *= rect.width / rect.height; x0 *= aspect; x1 *= aspect; // We want to ignore the scale, as scale doesn't affect the camera's view region in Unity Transform t = cam.transform; Quaternion rot = t.rotation; Vector3 pos = t.position; mSides[0] = rot * (new Vector3(x0, y0, depth)) + pos; mSides[1] = rot * (new Vector3(x0, y1, depth)) + pos; mSides[2] = rot * (new Vector3(x1, y1, depth)) + pos; mSides[3] = rot * (new Vector3(x1, y0, depth)) + pos; } else { mSides[0] = cam.ViewportToWorldPoint(new Vector3(0f, 0f, depth)); mSides[1] = cam.ViewportToWorldPoint(new Vector3(0f, 1f, depth)); mSides[2] = cam.ViewportToWorldPoint(new Vector3(1f, 1f, depth)); mSides[3] = cam.ViewportToWorldPoint(new Vector3(1f, 0f, depth)); } if (relativeTo != null) { for (int i = 0; i < 4; ++i) mSides[i] = relativeTo.InverseTransformPoint(mSides[i]); } return mSides; } /// <summary> /// Convenience function that converts Class + Function combo into Class.Function representation. /// </summary> static public string GetFuncName (object obj, string method) { if (obj == null) return "<null>"; string type = obj.GetType().ToString(); int period = type.LastIndexOf('/'); if (period > 0) type = type.Substring(period + 1); return string.IsNullOrEmpty(method) ? type : type + "/" + method; } #if UNITY_EDITOR || !UNITY_FLASH /// <summary> /// Execute the specified function on the target game object. /// </summary> static public void Execute<T> (GameObject go, string funcName) where T : Component { T[] comps = go.GetComponents<T>(); foreach (T comp in comps) { #if !UNITY_EDITOR && (UNITY_WEBPLAYER || UNITY_FLASH || UNITY_METRO || UNITY_WP8 || UNITY_WP_8_1) comp.SendMessage(funcName, SendMessageOptions.DontRequireReceiver); #else MethodInfo method = comp.GetType().GetMethod(funcName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) method.Invoke(comp, null); #endif } } /// <summary> /// Execute the specified function on the target game object and all of its children. /// </summary> static public void ExecuteAll<T> (GameObject root, string funcName) where T : Component { Execute<T>(root, funcName); Transform t = root.transform; for (int i = 0, imax = t.childCount; i < imax; ++i) ExecuteAll<T>(t.GetChild(i).gameObject, funcName); } /// <summary> /// Immediately start, update, and create all the draw calls from newly instantiated UI. /// This is useful if you plan on doing something like immediately taking a screenshot then destroying the UI. /// </summary> static public void ImmediatelyCreateDrawCalls (GameObject root) { ExecuteAll<UIWidget>(root, "Start"); ExecuteAll<UIPanel>(root, "Start"); ExecuteAll<UIWidget>(root, "Update"); ExecuteAll<UIPanel>(root, "Update"); ExecuteAll<UIPanel>(root, "LateUpdate"); } #endif #if UNITY_EDITOR static int mSizeFrame = -1; static System.Reflection.MethodInfo s_GetSizeOfMainGameView; static Vector2 mGameSize = Vector2.one; /// <summary> /// Size of the game view cannot be retrieved from Screen.width and Screen.height when the game view is hidden. /// </summary> static public Vector2 screenSize { get { int frame = Time.frameCount; if (mSizeFrame != frame || !Application.isPlaying) { mSizeFrame = frame; if (s_GetSizeOfMainGameView == null) { System.Type type = System.Type.GetType("UnityEditor.GameView,UnityEditor"); s_GetSizeOfMainGameView = type.GetMethod("GetSizeOfMainGameView", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); } mGameSize = (Vector2)s_GetSizeOfMainGameView.Invoke(null, null); } return mGameSize; } } #else /// <summary> /// Size of the game view cannot be retrieved from Screen.width and Screen.height when the game view is hidden. /// </summary> static public Vector2 screenSize { get { return new Vector2(Screen.width, Screen.height); } } #endif } 这个代码是Unity5.3.3f1版本的,现在升级到Unity 2017.4.0f1 (64-bit)版本 以下是报错信息Assets/NGUI/Scripts/Internal/NGUITools.cs(1438,37): error CS1519: Unexpected symbol `<' in class, struct, or interface member declaration ,把修改后的完整代码给我
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值